Changed Report to a message tree, recursively formatted

This commit is contained in:
Vhati 2013-08-26 15:01:21 -04:00
parent aa11d54396
commit c5dc2669cf
3 changed files with 129 additions and 99 deletions

View file

@ -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<ReportMessage> messages = new ArrayList<ReportMessage>();
List<ReportMessage> pendingMsgs = new ArrayList<ReportMessage>();
@ -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<ReportMessage> condensedList = new ArrayList<ReportMessage>();
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<ReportMessage> messages = new ArrayList<ReportMessage>();
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 );
}

View file

@ -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<ReportMessage> messages;
public final boolean outcome;
public Report( CharSequence text, boolean outcome ) {
this.text = text;
public Report( List<ReportMessage> messages, boolean outcome ) {
this.outcome = outcome;
List<ReportMessage> tmpList = new ArrayList<ReportMessage>( 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<ReportMessage> 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
/**
* Formats a list of messages.
*
* Leading newlines in the first message will be omitted.
*/
public void format( List<ReportMessage> 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 )
indent( message.text.subSequence( start, message.text.length() ), buf );
else
indent( message.text, buf );
return;
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<ReportMessage> messages, StringBuffer buf ) {
try { format( messages, (Appendable)buf ); }
public void format( List<ReportMessage> messages, StringBuffer buf, int indentCount ) {
try { format( messages, (Appendable)buf, indentCount ); }
catch( IOException e ) {}
}
/** Exception-swallowing wrapper. */
public void format( List<ReportMessage> messages, StringBuilder buf ) {
try { format( messages, (Appendable)buf ); }
public void format( List<ReportMessage> 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<ReportMessage> nestedMessages;
public ReportMessage( int type, CharSequence text ) {
this( type, text, new ArrayList() );
}
public ReportMessage( int type, CharSequence text, List<ReportMessage> nestedMessages ) {
this.type = type;
this.text = text;
List<ReportMessage> tmpList = new ArrayList<ReportMessage>( 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;
}
}

View file

@ -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;