001/*
002 * Copyright (C) 2022 - 2024, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.diagnostics;
017
018import static java.util.Collections.unmodifiableList;
019import static java.util.Objects.requireNonNull;
020
021import io.github.ascopes.jct.utils.ToStringBuilder;
022import java.time.Instant;
023import java.util.List;
024import java.util.Locale;
025import javax.tools.Diagnostic;
026import javax.tools.JavaFileObject;
027import org.apiguardian.api.API;
028import org.apiguardian.api.API.Status;
029import org.jspecify.annotations.Nullable;
030
031/**
032 * A wrapper around a {@link Diagnostic} which contains additional information about where the
033 * diagnostic was reported.
034 *
035 * @param <S> the file type.
036 * @author Ashley Scopes
037 * @since 0.0.1
038 */
039@API(since = "0.0.1", status = Status.STABLE)
040public class TraceDiagnostic<S extends JavaFileObject> implements Diagnostic<S> {
041
042  private final Instant timestamp;
043  private final long threadId;
044  private final @Nullable String threadName;
045  private final List<StackTraceElement> stackTrace;
046  private final Diagnostic<? extends S> original;
047
048  /**
049   * Initialize this diagnostic.
050   *
051   * @param timestamp  the timestamp.
052   * @param threadId   the thread ID.
053   * @param threadName the thread name, or {@code null} if not known.
054   * @param stackTrace the stacktrace.
055   * @param original   the original diagnostic that was reported.
056   */
057  public TraceDiagnostic(
058      Instant timestamp,
059      long threadId,
060      @Nullable String threadName,
061      List<StackTraceElement> stackTrace,
062      Diagnostic<? extends S> original
063  ) {
064    this.timestamp = requireNonNull(timestamp, "timestamp");
065    this.threadId = threadId;
066    this.threadName = threadName;
067    this.stackTrace = unmodifiableList(requireNonNull(stackTrace, "stackTrace"));
068    this.original = requireNonNull(original, "original");
069  }
070
071  @Override
072  public Kind getKind() {
073    return original.getKind();
074  }
075
076  @Nullable
077  @Override
078  public S getSource() {
079    return original.getSource();
080  }
081
082  @Override
083  public long getPosition() {
084    return original.getPosition();
085  }
086
087  @Override
088  public long getStartPosition() {
089    return original.getStartPosition();
090  }
091
092  @Override
093  public long getEndPosition() {
094    return original.getEndPosition();
095  }
096
097  @Override
098  public long getLineNumber() {
099    return original.getLineNumber();
100  }
101
102  @Override
103  public long getColumnNumber() {
104    return original.getColumnNumber();
105  }
106
107  @Nullable
108  @Override
109  public String getCode() {
110    return original.getCode();
111  }
112
113  @Override
114  public String getMessage(@Nullable Locale locale) {
115    return original.getMessage(locale);
116  }
117
118  /**
119   * Get the timestamp that the diagnostic was created at.
120   *
121   * @return the diagnostic timestamp.
122   */
123  public Instant getTimestamp() {
124    return timestamp;
125  }
126
127  /**
128   * Get the thread ID for the thread that created this diagnostic.
129   *
130   * @return the thread ID.
131   */
132  public long getThreadId() {
133    return threadId;
134  }
135
136  /**
137   * Get the thread name for the thread that created this diagnostic.
138   *
139   * @return the thread name, if known, or else {@code null}.
140   */
141  @Nullable
142  public String getThreadName() {
143    return threadName;
144  }
145
146  /**
147   * Get the stacktrace of where the diagnostic was written from.
148   *
149   * @return the stacktrace, in an unmodifiable list.
150   */
151  public List<StackTraceElement> getStackTrace() {
152    return stackTrace;
153  }
154
155  @Override
156  public String toString() {
157    return new ToStringBuilder(this)
158        .attribute("timestamp", timestamp)
159        .attribute("threadId", threadId)
160        .attribute("threadName", threadName)
161        .attribute("kind", original.getKind())
162        .attribute("code", original.getCode())
163        .attribute("column", original.getColumnNumber())
164        .attribute("line", original.getLineNumber())
165        .attribute("message", original.getMessage(Locale.ROOT))
166        .toString();
167  }
168}