diff options
author | Aurimas Liutikas <aurimas@google.com> | 2017-11-28 20:29:59 -0800 |
---|---|---|
committer | Aurimas Liutikas <aurimas@google.com> | 2017-11-28 20:29:59 -0800 |
commit | b4aec831a096b89efed151c9b2c5754d9491e6ea (patch) | |
tree | f8c04870544dda72e99139fcc713a66b04514dcc /src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java | |
parent | b8aa773e4886dabd0ffc5026420edd061069f044 (diff) | |
parent | dbbd00f58fd36c7c23e24e6652076b735b8e7c0c (diff) | |
download | checkstyle-b4aec831a096b89efed151c9b2c5754d9491e6ea.tar.gz |
Merge Checkstyle 8.5 into aosp/master
Test: None
Diffstat (limited to 'src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java')
-rw-r--r-- | src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java | 203 |
1 files changed, 169 insertions, 34 deletions
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java b/src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java index f3316f249..0a0095418 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/XMLLogger.java @@ -24,8 +24,13 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; import com.puppycrawl.tools.checkstyle.api.AuditEvent; import com.puppycrawl.tools.checkstyle.api.AuditListener; @@ -59,27 +64,41 @@ public class XMLLogger /** Close output stream in auditFinished. */ private final boolean closeStream; - /** Helper writer that allows easy encoding and printing. */ - private PrintWriter writer; + /** The writer lock object. */ + private final Object writerLock = new Object(); + + /** Holds all messages for the given file. */ + private final Map<String, FileMessages> fileMessages = + new ConcurrentHashMap<>(); + + /** + * Helper writer that allows easy encoding and printing. + */ + private final PrintWriter writer; /** * Creates a new {@code XMLLogger} instance. * Sets the output to a defined stream. * @param outputStream the stream to write logs to. * @param closeStream close oS in auditFinished + * @deprecated in order to fullfil demands of BooleanParameter IDEA check. + * @noinspection BooleanParameter */ + @Deprecated public XMLLogger(OutputStream outputStream, boolean closeStream) { - setOutputStream(outputStream); + writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); this.closeStream = closeStream; } /** - * Sets the OutputStream. - * @param outputStream the OutputStream to use - **/ - private void setOutputStream(OutputStream outputStream) { - final OutputStreamWriter osw = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - writer = new PrintWriter(osw); + * Creates a new {@code XMLLogger} instance. + * Sets the output to a defined stream. + * @param outputStream the stream to write logs to. + * @param outputStreamOptions if {@code CLOSE} stream should be closed in auditFinished() + */ + public XMLLogger(OutputStream outputStream, OutputStreamOptions outputStreamOptions) { + writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); + closeStream = outputStreamOptions == OutputStreamOptions.CLOSE; } @Override @@ -96,6 +115,8 @@ public class XMLLogger @Override public void auditFinished(AuditEvent event) { + fileMessages.forEach(this::writeFileMessages); + writer.println("</checkstyle>"); if (closeStream) { writer.close(); @@ -107,35 +128,116 @@ public class XMLLogger @Override public void fileStarted(AuditEvent event) { - writer.println("<file name=\"" + encode(event.getFileName()) + "\">"); + fileMessages.put(event.getFileName(), new FileMessages()); } @Override public void fileFinished(AuditEvent event) { + final String fileName = event.getFileName(); + final FileMessages messages = fileMessages.get(fileName); + + synchronized (writerLock) { + writeFileMessages(fileName, messages); + } + + fileMessages.remove(fileName); + } + + /** + * Prints the file section with all file errors and exceptions. + * @param fileName The file name, as should be printed in the opening file tag. + * @param messages The file messages. + */ + private void writeFileMessages(String fileName, FileMessages messages) { + writeFileOpeningTag(fileName); + if (messages != null) { + for (AuditEvent errorEvent : messages.getErrors()) { + writeFileError(errorEvent); + } + for (Throwable exception : messages.getExceptions()) { + writeException(exception); + } + } + writeFileClosingTag(); + } + + /** + * Prints the "file" opening tag with the given filename. + * @param fileName The filename to output. + */ + private void writeFileOpeningTag(String fileName) { + writer.println("<file name=\"" + encode(fileName) + "\">"); + } + + /** + * Prints the "file" closing tag. + */ + private void writeFileClosingTag() { writer.println("</file>"); } @Override public void addError(AuditEvent event) { if (event.getSeverityLevel() != SeverityLevel.IGNORE) { - writer.print("<error" + " line=\"" + event.getLine() + "\""); - if (event.getColumn() > 0) { - writer.print(" column=\"" + event.getColumn() + "\""); + final String fileName = event.getFileName(); + if (fileName == null) { + synchronized (writerLock) { + writeFileError(event); + } + } + else { + final FileMessages messages = fileMessages.computeIfAbsent( + fileName, name -> new FileMessages()); + messages.addError(event); } - writer.print(" severity=\"" + } + } + + /** + * Outputs the given envet to the writer. + * @param event An event to print. + */ + private void writeFileError(AuditEvent event) { + writer.print("<error" + " line=\"" + event.getLine() + "\""); + if (event.getColumn() > 0) { + writer.print(" column=\"" + event.getColumn() + "\""); + } + writer.print(" severity=\"" + event.getSeverityLevel().getName() + "\""); - writer.print(" message=\"" + writer.print(" message=\"" + encode(event.getMessage()) + "\""); - writer.println(" source=\"" - + encode(event.getSourceName()) - + "\"/>"); + writer.print(" source=\""); + if (event.getModuleId() == null) { + writer.print(encode(event.getSourceName())); + } + else { + writer.print(encode(event.getModuleId())); } + writer.println("\"/>"); } @Override public void addException(AuditEvent event, Throwable throwable) { + final String fileName = event.getFileName(); + if (fileName == null) { + synchronized (writerLock) { + writeException(throwable); + } + } + else { + final FileMessages messages = fileMessages.computeIfAbsent( + fileName, name -> new FileMessages()); + messages.addException(throwable); + } + } + + /** + * Writes the exception event to the print writer. + * @param throwable The + */ + private void writeException(Throwable throwable) { final StringWriter stringWriter = new StringWriter(); final PrintWriter printer = new PrintWriter(stringWriter); printer.println("<exception>"); @@ -152,7 +254,7 @@ public class XMLLogger * @return the escaped value if necessary. */ public static String encode(String value) { - final StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(256); for (int i = 0; i < value.length(); i++) { final char chr = value.charAt(i); switch (chr) { @@ -169,7 +271,7 @@ public class XMLLogger sb.append("""); break; case '&': - sb.append(encodeAmpersand(value, i)); + sb.append("&"); break; case '\r': break; @@ -177,7 +279,16 @@ public class XMLLogger sb.append(" "); break; default: - sb.append(chr); + if (Character.isISOControl(chr)) { + // true escape characters need '&' before but it also requires XML 1.1 + // until https://github.com/checkstyle/checkstyle/issues/5168 + sb.append("#x"); + sb.append(Integer.toHexString(chr)); + sb.append(';'); + } + else { + sb.append(chr); + } break; } } @@ -226,21 +337,45 @@ public class XMLLogger } /** - * Encodes ampersand in value at required position. - * @param value string value, which contains ampersand - * @param ampPosition position of ampersand in value - * @return encoded ampersand which should be used in xml + * The registered file messages. */ - private static String encodeAmpersand(String value, int ampPosition) { - final int nextSemi = value.indexOf(';', ampPosition); - final String result; - if (nextSemi == -1 - || !isReference(value.substring(ampPosition, nextSemi + 1))) { - result = "&"; + private static class FileMessages { + /** The file error events. */ + private final List<AuditEvent> errors = Collections.synchronizedList(new ArrayList<>()); + + /** The file exceptions. */ + private final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>()); + + /** + * Returns the file error events. + * @return the file error events. + */ + public List<AuditEvent> getErrors() { + return Collections.unmodifiableList(errors); } - else { - result = "&"; + + /** + * Adds the given error event to the messages. + * @param event the error event. + */ + public void addError(AuditEvent event) { + errors.add(event); + } + + /** + * Returns the file exceptions. + * @return the file exceptions. + */ + public List<Throwable> getExceptions() { + return Collections.unmodifiableList(exceptions); + } + + /** + * Adds the given exception to the messages. + * @param throwable the file exception + */ + public void addException(Throwable throwable) { + exceptions.add(throwable); } - return result; } } |