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.jspecify.annotations.Nullable;
028
029/**
030 * A wrapper around a {@link Diagnostic} which contains additional information about where the
031 * diagnostic was reported.
032 *
033 * @param <S> the file type.
034 * @author Ashley Scopes
035 * @since 0.0.1
036 */
037public class TraceDiagnostic<S extends JavaFileObject> implements Diagnostic<S> {
038
039  private final Instant timestamp;
040  private final long threadId;
041  private final @Nullable String threadName;
042  private final List<StackTraceElement> stackTrace;
043  private final Diagnostic<? extends S> original;
044
045  /**
046   * Initialize this diagnostic.
047   *
048   * @param timestamp  the timestamp.
049   * @param threadId   the thread ID.
050   * @param threadName the thread name, or {@code null} if not known.
051   * @param stackTrace the stacktrace.
052   * @param original   the original diagnostic that was reported.
053   */
054  public TraceDiagnostic(
055      Instant timestamp,
056      long threadId,
057      @Nullable String threadName,
058      List<StackTraceElement> stackTrace,
059      Diagnostic<? extends S> original
060  ) {
061    this.timestamp = requireNonNull(timestamp, "timestamp");
062    this.threadId = threadId;
063    this.threadName = threadName;
064    this.stackTrace = unmodifiableList(requireNonNull(stackTrace, "stackTrace"));
065    this.original = requireNonNull(original, "original");
066  }
067
068  @Override
069  public Kind getKind() {
070    return original.getKind();
071  }
072
073  @Nullable
074  @Override
075  public S getSource() {
076    return original.getSource();
077  }
078
079  @Override
080  public long getPosition() {
081    return original.getPosition();
082  }
083
084  @Override
085  public long getStartPosition() {
086    return original.getStartPosition();
087  }
088
089  @Override
090  public long getEndPosition() {
091    return original.getEndPosition();
092  }
093
094  @Override
095  public long getLineNumber() {
096    return original.getLineNumber();
097  }
098
099  @Override
100  public long getColumnNumber() {
101    return original.getColumnNumber();
102  }
103
104  @Nullable
105  @Override
106  public String getCode() {
107    return original.getCode();
108  }
109
110  @Override
111  public String getMessage(@Nullable Locale locale) {
112    return original.getMessage(locale);
113  }
114
115  /**
116   * Get the timestamp that the diagnostic was created at.
117   *
118   * @return the diagnostic timestamp.
119   */
120  public Instant getTimestamp() {
121    return timestamp;
122  }
123
124  /**
125   * Get the thread ID for the thread that created this diagnostic.
126   *
127   * @return the thread ID.
128   */
129  public long getThreadId() {
130    return threadId;
131  }
132
133  /**
134   * Get the thread name for the thread that created this diagnostic.
135   *
136   * @return the thread name, if known, or else {@code null}.
137   */
138  @Nullable
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}