001/*
002 * Copyright (C) 2022 - 2023, 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      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  @Override
077  public S getSource() {
078    return original.getSource();
079  }
080
081  @Override
082  public long getPosition() {
083    return original.getPosition();
084  }
085
086  @Override
087  public long getStartPosition() {
088    return original.getStartPosition();
089  }
090
091  @Override
092  public long getEndPosition() {
093    return original.getEndPosition();
094  }
095
096  @Override
097  public long getLineNumber() {
098    return original.getLineNumber();
099  }
100
101  @Override
102  public long getColumnNumber() {
103    return original.getColumnNumber();
104  }
105
106  @Override
107  public String getCode() {
108    return original.getCode();
109  }
110
111  @Override
112  public String getMessage(@Nullable Locale locale) {
113    return original.getMessage(locale);
114  }
115
116  /**
117   * Get the timestamp that the diagnostic was created at.
118   *
119   * @return the diagnostic timestamp.
120   */
121  public Instant getTimestamp() {
122    return timestamp;
123  }
124
125  /**
126   * Get the thread ID for the thread that created this diagnostic.
127   *
128   * @return the thread ID.
129   */
130  public long getThreadId() {
131    return threadId;
132  }
133
134  /**
135   * Get the thread name for the thread that created this diagnostic.
136   *
137   * @return the thread name, if known, or else {@code null}.
138   */
139  public String getThreadName() {
140    return threadName;
141  }
142
143  /**
144   * Get the stacktrace of where the diagnostic was written from.
145   *
146   * @return the stacktrace, in an unmodifiable list.
147   */
148  public List<StackTraceElement> getStackTrace() {
149    return stackTrace;
150  }
151
152  @Override
153  public String toString() {
154    return new ToStringBuilder(this)
155        .attribute("timestamp", timestamp)
156        .attribute("threadId", threadId)
157        .attribute("threadName", threadName)
158        .attribute("kind", original.getKind())
159        .attribute("code", original.getCode())
160        .attribute("column", original.getColumnNumber())
161        .attribute("line", original.getLineNumber())
162        .attribute("message", original.getMessage(Locale.ROOT))
163        .toString();
164  }
165}