From c5dc2669cf1f17469fd31e06770fe94f0439eeb6 Mon Sep 17 00:00:00 2001 From: Vhati Date: Mon, 26 Aug 2013 15:01:21 -0400 Subject: [PATCH] Changed Report to a message tree, recursively formatted --- .../vhati/modmanager/core/ModUtilities.java | 45 ++--- .../net/vhati/modmanager/core/Report.java | 176 +++++++++++------- .../net/vhati/modmanager/ui/ManagerFrame.java | 7 +- 3 files changed, 129 insertions(+), 99 deletions(-) diff --git a/src/main/java/net/vhati/modmanager/core/ModUtilities.java b/src/main/java/net/vhati/modmanager/core/ModUtilities.java index 55188ca..7e48ca1 100644 --- a/src/main/java/net/vhati/modmanager/core/ModUtilities.java +++ b/src/main/java/net/vhati/modmanager/core/ModUtilities.java @@ -27,7 +27,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import net.vhati.modmanager.core.Report; -import net.vhati.modmanager.core.Report.ReportFormatter; import net.vhati.modmanager.core.Report.ReportMessage; import ar.com.hjg.pngj.PngReader; @@ -199,10 +198,8 @@ public class ModUtilities { * Checks a mod file for common problems. * * @param modFile an *.ftl file to check - * @param formatter custom message decoration/indention, or null */ - public static Report validateModFile( File modFile, ReportFormatter formatter ) { - if ( formatter == null ) formatter = new ReportFormatter(); + public static Report validateModFile( File modFile ) { List messages = new ArrayList(); List pendingMsgs = new ArrayList(); @@ -392,16 +389,22 @@ public class ModUtilities { if ( isXML ) { if ( isXMLAppend ) seenAppend = true; - Report xmlReport = validateModXML( decodeResult.text, formatter ); + Report xmlReport = validateModXML( decodeResult.text ); + + if ( xmlReport.messages.size() > 0 ) { + List condensedList = new ArrayList(); + ReportMessage prevMessage = null; + for ( ReportMessage message : xmlReport.messages ) { + if ( !message.equals(prevMessage) ) { + condensedList.add( message ); + prevMessage = message; + } + } - if ( xmlReport.text.length() > 0 ) { pendingMsgs.add( new ReportMessage( ReportMessage.WARNING_SUBSECTION, - "XML Syntax Issues:" - ) ); - pendingMsgs.add( new ReportMessage( - ReportMessage.NESTED_BLOCK, - xmlReport.text + "XML Syntax Issues:", + condensedList ) ); } if ( xmlReport.outcome == false ) @@ -457,9 +460,7 @@ public class ModUtilities { String.format( "%s:", modFile.getName() ) ) ); - StringBuilder resultBuf = new StringBuilder(); - formatter.format( messages, resultBuf ); - return new Report( resultBuf, modValid ); + return new Report( messages, modValid ); } @@ -473,10 +474,8 @@ public class ModUtilities { * first typo it sees. :/ * * @param text unparsed xml - * @param formatter custom message decoration/indention, or null */ - public static Report validateModXML( String text, ReportFormatter formatter ) { - if ( formatter == null ) formatter = new ReportFormatter(); + public static Report validateModXML( String text ) { List messages = new ArrayList(); boolean xmlValid = true; @@ -711,17 +710,7 @@ public class ModUtilities { xmlValid = false; } - StringBuilder resultBuf = new StringBuilder(); - - ReportMessage prevMessage = null; - for ( ReportMessage message : messages ) { - if ( message.equals(prevMessage) ) continue; - - formatter.format( message, resultBuf ); - prevMessage = message; - } - - return new Report( resultBuf, xmlValid ); + return new Report( messages, xmlValid ); } diff --git a/src/main/java/net/vhati/modmanager/core/Report.java b/src/main/java/net/vhati/modmanager/core/Report.java index f63eb9a..668ce9a 100644 --- a/src/main/java/net/vhati/modmanager/core/Report.java +++ b/src/main/java/net/vhati/modmanager/core/Report.java @@ -1,37 +1,35 @@ package net.vhati.modmanager.core; import java.io.IOException; +import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * A human-readable block of text, with a boolean outcome. + * A list of messages, with a boolean outcome. */ public class Report { - public final CharSequence text; + public final List messages; public final boolean outcome; - public Report( CharSequence text, boolean outcome ) { - this.text = text; + public Report( List messages, boolean outcome ) { this.outcome = outcome; + + List tmpList = new ArrayList( messages ); + this.messages = Collections.unmodifiableList( tmpList ); } /** - * Formats ReportMessages to include in buffered reports. + * Formats ReportMessages. * * Symbols are prepended to indicate type. * - * Methods can accept a formatter as an argument, - * internally accumulate messages, format them, - * and return an Appendable CharSequence. - * - * To nest reports, that buffer can be intented - * and appended to another buffer; or it can be - * wrapped in a NESTED_BLOCK message of its own. + * Nested messages are indented. * * The Appendable interface claims to throw * IOException, but StringBuffer and StringBuilder @@ -55,31 +53,56 @@ public class Report { case ReportMessage.SUBSECTION: return "> "; case ReportMessage.WARNING_SUBSECTION: return "~ "; case ReportMessage.ERROR_SUBSECTION: return "! "; - case ReportMessage.NESTED_BLOCK: return ""; default: return getIndent(); } } - public void format( List messages, Appendable buf ) throws IOException { - for ( ReportMessage message : messages ) - format( message, buf ); + /** + * Returns the given CharSequence, or a new one decorated for the type. + */ + public CharSequence getDecoratedText( int messageType, CharSequence text ) { + // Sections get underlined. + if ( messageType == ReportMessage.SECTION ) { + StringBuilder buf = new StringBuilder( text.length()*2+1 ); + buf.append( text ).append( "\n" ); + + buf.append( getPrefix( messageType ).replaceAll( "\\S", " " ) ); + for ( int i=0; i < text.length(); i++ ) + buf.append( "-" ); + return buf; + } + else { + return text; + } } - public void format( ReportMessage message, Appendable buf ) throws IOException { - if ( message.type == ReportMessage.NESTED_BLOCK ) { - // Already formatted this once, indent it instead. - - // Skip leading newlines - int start = 0; - while ( start < message.text.length() && message.text.charAt(start) == '\n' ) - start++; - if ( start > 0 ) - indent( message.text.subSequence( start, message.text.length() ), buf ); - else - indent( message.text, buf ); - return; + /** + * Formats a list of messages. + * + * Leading newlines in the first message will be omitted. + */ + public void format( List messages, Appendable buf, int indentCount ) throws IOException { + for ( int i=0; i < messages.size(); i++ ) { + ReportMessage message = messages.get( i ); + if ( i == 0 ) { + // Skip leading newlines in first message. + int start = 0; + while ( start < message.text.length() && message.text.charAt(start) == '\n' ) + start++; + if ( start > 0 ) { + // Create a substitute message without them. + CharSequence newText = message.text.subSequence( start, message.text.length() ); + message = new ReportMessage( message.type, newText, message.nestedMessages ); + } + } + format( message, buf, indentCount ); } + } + /** + * Indents and decorates a message, then formats any nested messages. + */ + public void format( ReportMessage message, Appendable buf, int indentCount ) throws IOException { // Subsections get an extra linebreak above them. switch ( message.type ) { case ReportMessage.SUBSECTION: @@ -90,69 +113,61 @@ public class Report { // Not a subsection. } - buf.append( getPrefix( message.type ) ); - buf.append( message.text ); - buf.append( "\n" ); + CharSequence seq = getDecoratedText( message.type, message.text ); - // Sections get underlined. - if ( message.type == ReportMessage.SECTION ) { + // Indent the first line. + for ( int i=0; i < indentCount; i++ ) buf.append( getIndent() ); - for ( int i=0; i < message.text.length(); i++ ) - buf.append( "-" ); - buf.append( "\n" ); - } - } + buf.append( getPrefix( message.type ) ); - public void indent( CharSequence src, Appendable dst ) throws IOException { - Matcher m = breakPtn.matcher( src ); + // Indent multi-line message text. + Matcher m = breakPtn.matcher( seq ); int lastEnd = 0; while ( m.find() ) { if ( m.start() - lastEnd > 0 ) - dst.append( src.subSequence( lastEnd, m.start() ) ); + buf.append( seq.subSequence( lastEnd, m.start() ) ); - if ( m.group(1).length() > 0 ) // Didn't match beginning (^). - dst.append( "\n" ); - dst.append( getIndent() ); + if ( m.group(1).length() > 0 ) { + // At \n, instead of 0-length beginning (^). + buf.append( "\n" ); + for ( int i=0; i < indentCount; i++ ) { + buf.append( getIndent() ); + } + } lastEnd = m.end(); } - int srcLen = src.length(); + int srcLen = seq.length(); if ( lastEnd < srcLen ) - dst.append( src.subSequence( lastEnd, srcLen ) ); + buf.append( seq.subSequence( lastEnd, srcLen ) ); + + buf.append( "\n" ); + + if ( message.nestedMessages != null ) { + format( message.nestedMessages, buf, indentCount+1 ); + } } /** Exception-swallowing wrapper. */ - public void format( List messages, StringBuffer buf ) { - try { format( messages, (Appendable)buf ); } + public void format( List messages, StringBuffer buf, int indentCount ) { + try { format( messages, (Appendable)buf, indentCount ); } catch( IOException e ) {} } /** Exception-swallowing wrapper. */ - public void format( List messages, StringBuilder buf ) { - try { format( messages, (Appendable)buf ); } + public void format( List messages, StringBuilder buf, int indentCount ) { + try { format( messages, (Appendable)buf, indentCount ); } catch( IOException e ) {} } /** Exception-swallowing wrapper. */ - public void format( ReportMessage message, StringBuffer buf ) { - try { format( message, (Appendable)buf ); } + public void format( ReportMessage message, StringBuffer buf, int indentCount ) { + try { format( message, (Appendable)buf, indentCount ); } catch( IOException e ) {} } /** Exception-swallowing wrapper. */ - public void format( ReportMessage message, StringBuilder buf ) { - try { format( message, (Appendable)buf ); } - catch( IOException e ) {} - } - - /** Exception-swallowing wrapper. */ - public void indent( CharSequence src, StringBuffer dst ) { - try { indent( src, (Appendable)dst ); } - catch( IOException e ) {} - } - - /** Exception-swallowing wrapper. */ - public void indent( CharSequence src, StringBuilder dst ) { - try { indent( src, (Appendable)dst ); } + public void format( ReportMessage message, StringBuilder buf, int indentCount ) { + try { format( message, (Appendable)buf, indentCount ); } catch( IOException e ) {} } } @@ -174,14 +189,20 @@ public class Report { public static final int SUBSECTION = 5; public static final int WARNING_SUBSECTION = 6; public static final int ERROR_SUBSECTION = 7; - public static final int NESTED_BLOCK = 8; public final int type; public final CharSequence text; + public final List nestedMessages; public ReportMessage( int type, CharSequence text ) { + this( type, text, new ArrayList() ); + } + + public ReportMessage( int type, CharSequence text, List nestedMessages ) { this.type = type; this.text = text; + List tmpList = new ArrayList( nestedMessages ); + this.nestedMessages = Collections.unmodifiableList( tmpList ); } @Override @@ -190,16 +211,33 @@ public class Report { if ( o == this ) return true; if ( o instanceof ReportMessage == false ) return false; ReportMessage other = (ReportMessage)o; - return ( this.type == other.type && this.text.equals(other.text) ); + if ( this.type != other.type ) return false; + if ( this.text.equals(other.text) ) return false; + + if ( this.nestedMessages == null ) { + if ( other.nestedMessages != null ) + return false; + } else { + if ( !this.nestedMessages.equals( other.nestedMessages ) ) + return false; + } + return true; } @Override public int hashCode() { int result = 236; int salt = 778; + int nullCode = 99; result = salt * result + this.type; result = salt * result + text.hashCode(); + + if ( this.nestedMessages != null ) + result = salt * result + this.nestedMessages.hashCode(); + else + result = salt * result + nullCode; + return result; } } diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java index 0d638be..fed8c99 100644 --- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java +++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java @@ -60,6 +60,7 @@ import net.vhati.modmanager.core.ModPatchThread; import net.vhati.modmanager.core.ModPatchThread.BackedUpDat; import net.vhati.modmanager.core.ModUtilities; import net.vhati.modmanager.core.Report; +import net.vhati.modmanager.core.Report.ReportFormatter; import net.vhati.modmanager.json.GrognakCatalogFetcher; import net.vhati.modmanager.json.JacksonGrognakCatalogReader; import net.vhati.modmanager.ui.ChecklistTableModel; @@ -556,8 +557,10 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver if ( !localModsTableModel.isSelected(i) ) continue; ModFileInfo modFileInfo = localModsTableModel.getItem( i ); - Report validateReport = ModUtilities.validateModFile( modFileInfo.getFile(), null ); - resultBuf.append( validateReport.text ); + Report validateReport = ModUtilities.validateModFile( modFileInfo.getFile() ); + + ReportFormatter formatter = new ReportFormatter(); + formatter.format( validateReport.messages, resultBuf, 0 ); resultBuf.append( "\n" ); if ( validateReport.outcome == false ) anyInvalid = true;