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}