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

View file

@ -35,7 +35,9 @@ import net.vhati.modmanager.core.SloppyXMLParser;
import ar.com.hjg.pngj.PngReader;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.JDOMParseException;
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
* 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
* new third InputStream.
*
@ -182,13 +188,27 @@ public class ModUtilities {
* 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 {
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;
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;
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.
//
@ -197,10 +217,12 @@ public class ModUtilities {
Writer writer = new EOLWriter( new OutputStreamWriter( tmpData, encoder ), "\r\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( appendText );
writer.append( appendBuf );
writer.append( "\n" );
if ( mainHadRootTags ) writer.append( "</FTL>\n" );
writer.flush();
InputStream result = new ByteArrayInputStream( tmpData.toByteArray() );
@ -211,6 +233,10 @@ public class ModUtilities {
/**
* 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
* is returned as a new third InputStream.
*
@ -227,19 +253,43 @@ public class ModUtilities {
* @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 {
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;
mainText = xmlDeclPtn.matcher( mainText ).replaceFirst( "" );
mainText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ mainText +"</wrapper>";
Document mainDoc = parseStrictOrSloppyXML( mainText, mainDescription+" (wrapped)" );
buf = new StringBuffer( wrapperOpenTag.length() + mainText.length() + wrapperCloseTag.length() );
buf.append( wrapperOpenTag );
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;
Document mainDoc = parseStrictOrSloppyXML( buf, mainDescription+" (wrapped)" );
buf.setLength( 0 );
String appendText = decodeText( appendStream, appendDescription ).text;
appendText = xmlDeclPtn.matcher( appendText ).replaceFirst( "" );
appendText = "<wrapper xmlns:mod='mod' xmlns:mod-append='mod-append' xmlns:mod-overwrite='mod-overwrite'>"+ appendText +"</wrapper>";
Document appendDoc = parseStrictOrSloppyXML( appendText, appendDescription+" (wrapped)" );
buf.ensureCapacity( wrapperOpenTag.length() + appendText.length() + wrapperCloseTag.length() );
buf.append( wrapperOpenTag );
m = comboPtn.matcher( appendText );
while ( m.find() ) {
m.appendReplacement( buf, "" );
}
m.appendTail( buf );
buf.append( wrapperCloseTag );
appendText = null;
Document appendDoc = parseStrictOrSloppyXML( buf, appendDescription+" (wrapped)" );
buf.setLength( 0 );
buf.trimToSize(); // Free the buffer.
buf = null;
XMLPatcher patcher = new XMLPatcher();
patcher.setGlobalPanic( globalPanic );
@ -247,6 +297,20 @@ public class ModUtilities {
mainDoc = 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.
// 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.KeyEvent;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
@ -63,7 +64,6 @@ import net.vhati.modmanager.core.SloppyXMLOutputProcessor;
import net.vhati.modmanager.core.XMLPatcher;
import net.vhati.modmanager.ui.ClipboardMenuMouseListener;
import org.jdom2.Document;
import org.jdom2.JDOMException;
@ -73,7 +73,7 @@ import org.jdom2.JDOMException;
public class ModXMLSandbox extends JFrame implements ActionListener {
private UndoManager undoManager = new UndoManager();
private Document mainDoc = null;
private String mainText = null;
private File datsDir;
@ -344,17 +344,11 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
if ( innerPath == null ) return;
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();
mainText = mainText.replaceFirst( "<[?]xml [^>]*?[?]>", "" );
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.setText( rebuiltText );
mainArea.setCaretPosition( 0 );
areasPane.setSelectedComponent( mainScroll );
resultArea.setText( "" );
@ -378,29 +372,26 @@ public class ModXMLSandbox extends JFrame implements ActionListener {
private void patch() {
if ( mainDoc == null ) return;
String mainText = mainArea.getText();
if ( mainText.length() == 0 ) return;
messageArea.setText( "" );
try {
InputStream mainStream = new ByteArrayInputStream( mainText.getBytes( "UTF-8" ) );
String appendText = appendArea.getText();
appendText = appendText.replaceFirst( "<[?]xml [^>]*?[?]>", "" );
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" );
InputStream appendStream = new ByteArrayInputStream( appendText.getBytes( "UTF-8" ) );
XMLPatcher patcher = new XMLPatcher();
patcher.setGlobalPanic( false );
Document resultDoc = patcher.patch( mainDoc, appendDoc );
InputStream resultStream = ModUtilities.patchXMLFile( mainStream, appendStream, "windows-1252", false, "Sandbox Main XML", "Sandbox Append XML" );
String resultText = ModUtilities.decodeText( resultStream, "Sandbox Result XML" ).text;
StringWriter writer = new StringWriter();
SloppyXMLOutputProcessor.sloppyPrint( resultDoc, writer, null );
String displayedText = writer.toString().replaceAll( "\r(?!\n)|(?<!\r)\n|\r\n", "\n" ); // sloppyPrint needs normalizing!?
resultArea.setText( displayedText );
resultArea.setText( resultText );
resultArea.setCaretPosition( 0 );
areasPane.setSelectedComponent( resultScroll );
}
catch ( Exception f ) {
messageArea.setText( f.getMessage() );
catch ( Exception e ) {
messageArea.setText( e.toString() );
messageArea.setCaretPosition( 0 );
}
}