Added tolerance of optional FTL 1.6.1 root tags when patching

This commit is contained in:
Vhati 2017-12-05 22:42:31 -05:00
parent 8f8d2072a9
commit a5383766ef
3 changed files with 101 additions and 40 deletions

View file

@ -39,6 +39,12 @@ The Append Extension
When you're not overriding something, try to use unique names, so that When you're not overriding something, try to use unique names, so that
it won't clobber another mod and vice versa. it won't clobber another mod and vice versa.
FTL 1.6.1 introduced <FTL>...</FTL> root tags wrapping XML files. If present,
Slipstream will remove them, append, and put them back afterward. Special
tags (see "Advanced XML" below) will be unaware of them. Mod files are not
required to include these root tags, though they can. Slipstream will remove
those as well.
General General

View file

@ -35,7 +35,9 @@ import net.vhati.modmanager.core.SloppyXMLParser;
import ar.com.hjg.pngj.PngReader; import ar.com.hjg.pngj.PngReader;
import org.jdom2.Content;
import org.jdom2.Document; import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException; import org.jdom2.JDOMException;
import org.jdom2.input.JDOMParseException; import org.jdom2.input.JDOMParseException;
import org.jdom2.input.SAXBuilder; import org.jdom2.input.SAXBuilder;
@ -166,6 +168,10 @@ public class ModUtilities {
* tacked on as-is. Any xml declaration tags will be scrubbed from both * tacked on as-is. Any xml declaration tags will be scrubbed from both
* streams, and a new one will be prepended. * streams, and a new one will be prepended.
* *
* If the mainStream had &lt;FTL&gt; tags (introduced in FTL 1.6.1), they
* will be scrubbed, and new ones will be added after appending. If
* appendStream has those tags, they will be scrubbed.
*
* The two InputStreams are read, and the combined result is returned as a * The two InputStreams are read, and the combined result is returned as a
* new third InputStream. * new third InputStream.
* *
@ -182,13 +188,27 @@ public class ModUtilities {
* the source of new content to append as the first argument). * the source of new content to append as the first argument).
*/ */
public static InputStream appendXMLFile( InputStream mainStream, InputStream appendStream, String encoding, String mainDescription, String appendDescription ) throws IOException { public static InputStream appendXMLFile( InputStream mainStream, InputStream appendStream, String encoding, String mainDescription, String appendDescription ) throws IOException {
Pattern xmlDeclPtn = Pattern.compile( "<[?]xml [^>]*?[?]>\n*" ); // XML declaration, or root FTL tags.
Pattern comboPtn = Pattern.compile( "(<[?]xml [^>]*?[?]>\n*)|(</?FTL>)" );
Matcher m = null;
boolean mainHadRootTags = false;
String mainText = decodeText( mainStream, mainDescription ).text; String mainText = decodeText( mainStream, mainDescription ).text;
mainText = xmlDeclPtn.matcher( mainText ).replaceFirst( "" ); StringBuffer mainBuf = new StringBuffer( mainText.length() );
m = comboPtn.matcher( mainText );
while ( m.find() ) {
if ( m.group( 2 ) != null ) mainHadRootTags = true;
m.appendReplacement( mainBuf, "" );
}
m.appendTail( mainBuf );
String appendText = decodeText( appendStream, appendDescription ).text; String appendText = decodeText( appendStream, appendDescription ).text;
appendText = xmlDeclPtn.matcher( appendText ).replaceFirst( "" ); StringBuffer appendBuf = new StringBuffer( appendText.length() );
m = comboPtn.matcher( appendText );
while ( m.find() ) {
m.appendReplacement( appendBuf, "" );
}
m.appendTail( appendBuf );
// Concatenate, filtering the stream to standardize newlines and encode. // Concatenate, filtering the stream to standardize newlines and encode.
// //
@ -197,10 +217,12 @@ public class ModUtilities {
Writer writer = new EOLWriter( new OutputStreamWriter( tmpData, encoder ), "\r\n" ); Writer writer = new EOLWriter( new OutputStreamWriter( tmpData, encoder ), "\r\n" );
writer.append( "<?xml version=\"1.0\" encoding=\""+ encoding +"\"?>\n" ); writer.append( "<?xml version=\"1.0\" encoding=\""+ encoding +"\"?>\n" );
writer.append( mainText ); if ( mainHadRootTags ) writer.append( "<FTL>\n" );
writer.append( mainBuf );
writer.append( "\n\n<!-- Appended by Slipstream -->\n\n" ); writer.append( "\n\n<!-- Appended by Slipstream -->\n\n" );
writer.append( appendText ); writer.append( appendBuf );
writer.append( "\n" ); writer.append( "\n" );
if ( mainHadRootTags ) writer.append( "</FTL>\n" );
writer.flush(); writer.flush();
InputStream result = new ByteArrayInputStream( tmpData.toByteArray() ); InputStream result = new ByteArrayInputStream( tmpData.toByteArray() );
@ -211,6 +233,10 @@ public class ModUtilities {
/** /**
* Appends and modifies mainStream, using content from appendStream. * Appends and modifies mainStream, using content from appendStream.
* *
* If the mainStream had &lt;FTL&gt; tags (introduced in FTL 1.6.1), they
* will be scrubbed, and new ones will be added after appending. If
* appendStream has those tags, they will be scrubbed.
*
* The two InputStreams are read, and the combined result * The two InputStreams are read, and the combined result
* is returned as a new third InputStream. * is returned as a new third InputStream.
* *
@ -227,19 +253,43 @@ public class ModUtilities {
* @see net.vhati.modmanager.core.SloppyXMLOutputProcessor * @see net.vhati.modmanager.core.SloppyXMLOutputProcessor
*/ */
public static InputStream patchXMLFile( InputStream mainStream, InputStream appendStream, String encoding, boolean globalPanic, String mainDescription, String appendDescription ) throws IOException, JDOMException { public static InputStream patchXMLFile( InputStream mainStream, InputStream appendStream, String encoding, boolean globalPanic, String mainDescription, String appendDescription ) throws IOException, JDOMException {
Pattern xmlDeclPtn = Pattern.compile( "<[?]xml [^>]*?[?]>\n*" ); // XML declaration, or root FTL tags.
Pattern comboPtn = Pattern.compile( "(<[?]xml [^>]*?[?]>\n*)|(</?FTL>)" );
Matcher m = null;
boolean mainHadRootTags = false;
String wrapperOpenTag = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>";
String wrapperCloseTag = "</wrapper>";
StringBuffer buf = null;
String mainText = decodeText( mainStream, mainDescription ).text; String mainText = decodeText( mainStream, mainDescription ).text;
mainText = xmlDeclPtn.matcher( mainText ).replaceFirst( "" ); buf = new StringBuffer( wrapperOpenTag.length() + mainText.length() + wrapperCloseTag.length() );
mainText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ mainText +"</wrapper>"; buf.append( wrapperOpenTag );
Document mainDoc = parseStrictOrSloppyXML( mainText, mainDescription+" (wrapped)" ); m = comboPtn.matcher( mainText );
while ( m.find() ) {
if ( m.group( 2 ) != null ) mainHadRootTags = true;
m.appendReplacement( buf, "" );
}
m.appendTail( buf );
buf.append( wrapperCloseTag );
mainText = null; mainText = null;
Document mainDoc = parseStrictOrSloppyXML( buf, mainDescription+" (wrapped)" );
buf.setLength( 0 );
String appendText = decodeText( appendStream, appendDescription ).text; String appendText = decodeText( appendStream, appendDescription ).text;
appendText = xmlDeclPtn.matcher( appendText ).replaceFirst( "" ); buf.ensureCapacity( wrapperOpenTag.length() + appendText.length() + wrapperCloseTag.length() );
appendText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ appendText +"</wrapper>"; buf.append( wrapperOpenTag );
Document appendDoc = parseStrictOrSloppyXML( appendText, appendDescription+" (wrapped)" ); m = comboPtn.matcher( appendText );
while ( m.find() ) {
m.appendReplacement( buf, "" );
}
m.appendTail( buf );
buf.append( wrapperCloseTag );
appendText = null; appendText = null;
Document appendDoc = parseStrictOrSloppyXML( buf, appendDescription+" (wrapped)" );
buf.setLength( 0 );
buf.trimToSize(); // Free the buffer.
buf = null;
XMLPatcher patcher = new XMLPatcher(); XMLPatcher patcher = new XMLPatcher();
patcher.setGlobalPanic( globalPanic ); patcher.setGlobalPanic( globalPanic );
@ -247,6 +297,20 @@ public class ModUtilities {
mainDoc = null; mainDoc = null;
appendDoc = null; appendDoc = null;
// Add FTL tags and move all content inside them.
// Collect live getContent() results in an Arraylist to avoid
// ConcurrentModificationException when detaching in the loop.
if ( mainHadRootTags ) {
Element mergedRoot = mergedDoc.getRootElement();
Element ftlNode = new Element( "FTL" );
List<Content> mergedContentList = new ArrayList<Content>( mergedRoot.getContent() );
for ( Content c : mergedContentList ) { //
c.detach();
}
ftlNode.addContent( mergedContentList );
mergedRoot.addContent( ftlNode );
}
// Bake XML into text, filtering the stream to standardize newlines and encode. // Bake XML into text, filtering the stream to standardize newlines and encode.
// TODO: sloppyPrint() needs EOL normalizing!? // TODO: sloppyPrint() needs EOL normalizing!?
// //

View file

@ -11,6 +11,7 @@ import java.awt.event.FocusEvent;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.io.File; import java.io.File;
import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -63,7 +64,6 @@ import net.vhati.modmanager.core.SloppyXMLOutputProcessor;
import net.vhati.modmanager.core.XMLPatcher; import net.vhati.modmanager.core.XMLPatcher;
import net.vhati.modmanager.ui.ClipboardMenuMouseListener; import net.vhati.modmanager.ui.ClipboardMenuMouseListener;
import org.jdom2.Document;
import org.jdom2.JDOMException; import org.jdom2.JDOMException;
@ -73,7 +73,7 @@ import org.jdom2.JDOMException;
public class ModXMLSandbox extends JFrame implements ActionListener { public class ModXMLSandbox extends JFrame implements ActionListener {
private UndoManager undoManager = new UndoManager(); private UndoManager undoManager = new UndoManager();
private Document mainDoc = null; private String mainText = null;
private File datsDir; private File datsDir;
@ -344,17 +344,11 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
if ( innerPath == null ) return; if ( innerPath == null ) return;
is = pack.getInputStream( innerPath ); is = pack.getInputStream( innerPath );
String mainText = ModUtilities.decodeText( is, pack.getName()+":"+innerPath ).text; InputStream rebuiltStream = ModUtilities.rebuildXMLFile( is, "windows-1252", pack.getName()+":"+innerPath );
String rebuiltText = ModUtilities.decodeText( rebuiltStream, "Sandbox Main XML" ).text;
is.close(); is.close();
mainText = mainText.replaceFirst( "<[?]xml [^>]*?[?]>", "" ); mainArea.setText( rebuiltText );
mainText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ mainText +"</wrapper>";
mainDoc = ModUtilities.parseStrictOrSloppyXML( mainText, "Sandbox Main XML" );
StringWriter writer = new StringWriter();
SloppyXMLOutputProcessor.sloppyPrint( mainDoc, writer, null );
String displayedText = writer.toString().replaceAll( "\r(?!\n)|(?<!\r)\n|\r\n", "\n" ); // sloppyPrint needs normalizing!?
mainArea.setText( displayedText );
mainArea.setCaretPosition( 0 ); mainArea.setCaretPosition( 0 );
areasPane.setSelectedComponent( mainScroll ); areasPane.setSelectedComponent( mainScroll );
resultArea.setText( "" ); resultArea.setText( "" );
@ -378,29 +372,26 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
private void patch() { private void patch() {
if ( mainDoc == null ) return; String mainText = mainArea.getText();
if ( mainText.length() == 0 ) return;
messageArea.setText( "" ); messageArea.setText( "" );
try { try {
InputStream mainStream = new ByteArrayInputStream( mainText.getBytes( "UTF-8" ) );
String appendText = appendArea.getText(); String appendText = appendArea.getText();
appendText = appendText.replaceFirst( "<[?]xml [^>]*?[?]>", "" ); InputStream appendStream = new ByteArrayInputStream( appendText.getBytes( "UTF-8" ) );
appendText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ appendText +"</wrapper>";
Document appendDoc = ModUtilities.parseStrictOrSloppyXML( appendText, "Sandbox Append XML" );
XMLPatcher patcher = new XMLPatcher(); InputStream resultStream = ModUtilities.patchXMLFile( mainStream, appendStream, "windows-1252", false, "Sandbox Main XML", "Sandbox Append XML" );
patcher.setGlobalPanic( false ); String resultText = ModUtilities.decodeText( resultStream, "Sandbox Result XML" ).text;
Document resultDoc = patcher.patch( mainDoc, appendDoc );
StringWriter writer = new StringWriter(); resultArea.setText( resultText );
SloppyXMLOutputProcessor.sloppyPrint( resultDoc, writer, null );
String displayedText = writer.toString().replaceAll( "\r(?!\n)|(?<!\r)\n|\r\n", "\n" ); // sloppyPrint needs normalizing!?
resultArea.setText( displayedText );
resultArea.setCaretPosition( 0 ); resultArea.setCaretPosition( 0 );
areasPane.setSelectedComponent( resultScroll ); areasPane.setSelectedComponent( resultScroll );
} }
catch ( Exception f ) { catch ( Exception e ) {
messageArea.setText( f.getMessage() ); messageArea.setText( e.toString() );
messageArea.setCaretPosition( 0 ); messageArea.setCaretPosition( 0 );
} }
} }