/* XMLElement.java
*
* $Revision: 1.2 $
* $Date: 2002/08/03 04:36:34 $
* $Name: $
*
* This file is part of NanoXML 2 Lite.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*****************************************************************************/
/* JAM: hacked the source to remove unneeded methods and comments. */
package net.sourceforge.nanoxml;
import java.io.*;
import java.util.*;
import net.sourceforge.jnlp.runtime.JNLPRuntime;
import net.sourceforge.jnlp.util.logging.OutputController;
/**
* XMLElement is a representation of an XML object. The object is able to parse
* XML code.
*
* - Parsing XML Data
* -
* You can parse XML data using the following code:
*
{@code
* XMLElement xml = new XMLElement();
* FileReader reader = new FileReader("filename.xml");
* xml.parseFromReader(reader);
* }
* - Retrieving Attributes
* -
* You can enumerate the attributes of an element using the method
* {@link #enumerateAttributeNames() enumerateAttributeNames}.
* The attribute values can be retrieved using the method
* {@link #getAttribute(java.lang.String) getAttribute}.
* The following example shows how to list the attributes of an element:
*
{@code
* XMLElement element = ...;
* Enumeration enum = element.enumerateAttributeNames();
* while (enum.hasMoreElements()) {
* String key = (String) enum.nextElement();
* String value = (String) element.getAttribute(key);
* System.out.println(key + " = " + value);
* }
* }
* - Retrieving Child Elements
* -
* You can enumerate the children of an element using
* {@link #enumerateChildren() enumerateChildren}.
* The number of child elements can be retrieved using
* {@link #countChildren() countChildren}.
*
* - Elements Containing Character Data
* -
* If an elements contains character data, like in the following example:
*
* you can retrieve that data using the method
* {@link #getContent() getContent}.
*
* - Subclassing XMLElement
* -
* When subclassing XMLElement, you need to override the method
* {@link #createAnotherElement() createAnotherElement}
* which has to return a new copy of the receiver.
*
*
*
* @see net.sourceforge.nanoxml.XMLParseException
*
* @author Marc De Scheemaecker
* <cyberelf@mac.com>
* @version $Name: $, $Revision: 1.2 $
*/
public class XMLElement {
/**
* The attributes given to the element.
*
*
- Invariants:
-
*
- The field can be empty.
*
- The field is never
null
.
* - The keys and the values are strings.
*
*/
private Hashtable attributes;
/**
* Child elements of the element.
*
* - Invariants:
-
*
- The field can be empty.
*
- The field is never
null
.
* - The elements are instances of
XMLElement
* or a subclass of XMLElement
.
*
*/
private Vector children;
/**
* The name of the element.
*
* - Invariants:
-
*
- The field is
null
iff the element is not
* initialized by either parse or setName.
* - If the field is not
null
, it's not empty.
* - If the field is not
null
, it contains a valid
* XML identifier.
*
*/
private String name;
/**
* The #PCDATA content of the object.
*
* - Invariants:
-
*
- The field is
null
iff the element is not a
* #PCDATA element.
* - The field can be any string, including the empty string.
*
*/
private String contents;
/**
* Conversion table for &...; entities. The keys are the entity names
* without the & and ; delimiters.
*
* - Invariants:
-
*
- The field is never
null
.
* - The field always contains the following associations:
* "lt" => "<", "gt" => ">",
* "quot" => "\"", "apos" => "'",
* "amp" => "&"
*
- The keys are strings
*
- The values are char arrays
*
*/
private Hashtable entities;
/**
* The line number where the element starts.
*
* - Invariants:
-
*
*/
private int lineNr;
/**
* true
if the case of the element and attribute names
* are case insensitive.
*/
private boolean ignoreCase;
/**
* true
if the leading and trailing whitespace of #PCDATA
* sections have to be ignored.
*/
private boolean ignoreWhitespace;
/**
* Character read too much.
* This character provides push-back functionality to the input reader
* without having to use a PushbackReader.
* If there is no such character, this field is '\0'.
*/
private char charReadTooMuch;
/**
* Character read too much for the comment remover.
*/
private char sanitizeCharReadTooMuch;
/**
* The reader provided by the caller of the parse method.
*
* - Invariants:
-
*
- The field is not
null
while the parse method
* is running.
*
*/
private Reader reader;
/**
* The current line number in the source content.
*
* - Invariants:
-
*
- parserLineNr > 0 while the parse method is running.
*
*/
private int parserLineNr;
/**
* Creates and initializes a new XML element.
* Calling the construction is equivalent to:
* new XMLElement(new Hashtable(), false, true)
*
*
* - Postconditions:
-
*
- countChildren() => 0
*
- enumerateChildren() => empty enumeration
*
- enumeratePropertyNames() => empty enumeration
*
- getChildren() => empty vector
*
- getContent() => ""
*
- getLineNr() => 0
*
- getName() => null
*
*
*/
public XMLElement() {
this(new Hashtable(), false, true, true);
}
/**
* Creates and initializes a new XML element.
*
* This constructor should only be called from
* {@link #createAnotherElement() createAnotherElement}
* to create child elements.
*
* @param entities
* The entity conversion table.
* @param skipLeadingWhitespace
* true
if leading and trailing whitespace in PCDATA
* content has to be removed.
* @param fillBasicConversionTable
* true
if the basic entities need to be added to
* the entity list (client code calling this constructor).
* @param ignoreCase
* true
if the case of element and attribute names have
* to be ignored.
*
*
- Preconditions:
-
*
entities != null
* - if
fillBasicConversionTable == false
* then entities
contains at least the following
* entries: amp
, lt
, gt
,
* apos
and quot
*
*
* - Postconditions:
-
*
- countChildren() => 0
*
- enumerateChildren() => empty enumeration
*
- enumeratePropertyNames() => empty enumeration
*
- getChildren() => empty vector
*
- getContent() => ""
*
- getLineNr() => 0
*
- getName() => null
*
*
*/
protected XMLElement(Hashtable entities,
boolean skipLeadingWhitespace,
boolean fillBasicConversionTable,
boolean ignoreCase) {
this.ignoreWhitespace = skipLeadingWhitespace;
this.ignoreCase = ignoreCase;
this.name = null;
this.contents = "";
this.attributes = new Hashtable();
this.children = new Vector();
this.entities = entities;
this.lineNr = 0;
Enumeration e = this.entities.keys();
while (e.hasMoreElements()) {
String key = e.nextElement();
Object value = this.entities.get(key);
if (value instanceof String) {
entities.put(key, ((String) value).toCharArray());
}
}
if (fillBasicConversionTable) {
this.entities.put("amp", new char[] { '&' });
this.entities.put("quot", new char[] { '"' });
this.entities.put("apos", new char[] { '\'' });
this.entities.put("lt", new char[] { '<' });
this.entities.put("gt", new char[] { '>' });
}
}
/**
* Adds a child element.
*
* @param child
* The child element to add.
*
* - Preconditions:
-
*
child != null
* child.getName() != null
* child
does not have a parent element
*
*
* - Postconditions:
-
*
- countChildren() => old.countChildren() + 1
*
- enumerateChildren() => old.enumerateChildren() + child
*
- getChildren() => old.enumerateChildren() + child
*
*
*/
public void addChild(XMLElement child) {
this.children.addElement(child);
}
/**
* Adds or modifies an attribute.
*
* @param name
* The name of the attribute.
* @param value
* The value of the attribute.
*
* - Preconditions:
-
*
name != null
* name
is a valid XML identifier
* value != null
*
*
* - Postconditions:
-
*
- enumerateAttributeNames()
* => old.enumerateAttributeNames() + name
*
- getAttribute(name) => value
*
*
*/
public void setAttribute(String name,
Object value) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
this.attributes.put(name, value.toString());
}
/**
* Returns the number of child elements of the element.
*
* - Postconditions:
-
*
*
*/
public int countChildren() {
return this.children.size();
}
/**
* Enumerates the attribute names.
*
* - Postconditions:
-
*
*
*/
public Enumeration enumerateAttributeNames() {
return this.attributes.keys();
}
/**
* Enumerates the child elements.
*
* - Postconditions:
-
*
*
*/
public Enumeration enumerateChildren() {
return this.children.elements();
}
/**
* Returns the PCDATA content of the object. If there is no such content,
* null
is returned.
*
*/
public String getContent() {
return this.contents;
}
/**
* Returns the line nr in the source data on which the element is found.
* This method returns 0
there is no associated source data.
*
* - Postconditions:
-
*
*/
public int getLineNr() {
return this.lineNr;
}
/**
* Returns an attribute of the element.
* If the attribute doesn't exist, null
is returned.
*
* @param name The name of the attribute.
*
* - Preconditions:
-
*
name != null
* name
is a valid XML identifier
*
*
*/
public Object getAttribute(String name) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object value = this.attributes.get(name);
return value;
}
/**
* Returns the name of the element.
*
*/
public String getName() {
return this.name;
}
/**
* Reads one XML element from a java.io.Reader and parses it.
*
* @param reader
* The reader from which to retrieve the XML data.
*
* - Preconditions:
-
*
reader != null
* reader
is not closed
*
*
* - Postconditions:
-
*
- the state of the receiver is updated to reflect the XML element
* parsed from the reader
*
- the reader points to the first character following the last
* '>' character of the XML element
*
*
* @throws java.io.IOException
* If an error occured while reading the input.
* @throws net.sourceforge.nanoxml.XMLParseException
* If an error occured while parsing the read data.
*/
public void parseFromReader(Reader reader)
throws IOException, XMLParseException {
this.parseFromReader(reader, /*startingLineNr*/1);
}
/**
* Reads one XML element from a java.io.Reader and parses it.
*
* @param reader
* The reader from which to retrieve the XML data.
* @param startingLineNr
* The line number of the first line in the data.
*
* - Preconditions:
-
*
reader != null
* reader
is not closed
*
*
* - Postconditions:
-
*
- the state of the receiver is updated to reflect the XML element
* parsed from the reader
*
- the reader points to the first character following the last
* '>' character of the XML element
*
*
* @throws java.io.IOException
* If an error occured while reading the input.
* @throws net.sourceforge.nanoxml.XMLParseException
* If an error occured while parsing the read data.
*/
public void parseFromReader(Reader reader,
int startingLineNr)
throws IOException, XMLParseException {
this.charReadTooMuch = '\0';
this.reader = reader;
this.parserLineNr = startingLineNr;
for (;;) {
char ch = this.scanWhitespace();
if (ch != '<') {
throw this.expectedInput("<", ch);
}
ch = this.readChar();
if ((ch == '!') || (ch == '?')) {
this.skipSpecialTag(0);
} else {
this.unreadChar(ch);
this.scanElement(this);
return;
}
}
}
/**
* Creates a new similar XML element.
*
* You should override this method when subclassing XMLElement.
*/
protected XMLElement createAnotherElement() {
return new XMLElement(this.entities,
this.ignoreWhitespace,
false,
this.ignoreCase);
}
/**
* Changes the content string.
*
* @param content
* The new content string.
*/
public void setContent(String content) {
this.contents = content;
}
/**
* Changes the name of the element.
*
* @param name
* The new name.
*
*
- Preconditions:
-
*
name != null
* name
is a valid XML identifier
*
*
*/
public void setName(String name) {
this.name = name;
}
/**
* Scans an identifier from the current reader.
* The scanned identifier is appended to result
.
*
* @param result
* The buffer in which the scanned identifier will be put.
*
* - Preconditions:
-
*
result != null
* - The next character read from the reader is a valid first
* character of an XML identifier.
*
*
* - Postconditions:
-
*
- The next character read from the reader won't be an identifier
* character.
*
*/
protected void scanIdentifier(StringBuffer result)
throws IOException {
for (;;) {
char ch = this.readChar();
if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z'))
&& ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.')
&& (ch != ':') && (ch != '-') && (ch <= '\u007E')) {
this.unreadChar(ch);
return;
}
result.append(ch);
}
}
/**
* This method scans an identifier from the current reader.
*
* @return the next character following the whitespace.
*/
protected char scanWhitespace()
throws IOException {
for (;;) {
char ch = this.readChar();
switch (ch) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
return ch;
}
}
}
/**
* This method scans an identifier from the current reader.
* The scanned whitespace is appended to result
.
*
* @return the next character following the whitespace.
*
* - Preconditions:
-
*
*/
protected char scanWhitespace(StringBuffer result)
throws IOException {
for (;;) {
char ch = this.readChar();
switch (ch) {
case ' ':
case '\t':
case '\n':
result.append(ch);
break;
case '\r':
break;
default:
return ch;
}
}
}
/**
* This method scans a delimited string from the current reader.
* The scanned string without delimiters is appended to
* string
.
*
* - Preconditions:
-
*
string != null
* - the next char read is the string delimiter
*
*/
protected void scanString(StringBuffer string)
throws IOException {
char delimiter = this.readChar();
if ((delimiter != '\'') && (delimiter != '"')) {
throw this.expectedInput("' or \"");
}
for (;;) {
char ch = this.readChar();
if (ch == delimiter) {
return;
} else if (ch == '&') {
this.resolveEntity(string);
} else {
string.append(ch);
}
}
}
/**
* Scans a #PCDATA element. CDATA sections and entities are resolved.
* The next < char is skipped.
* The scanned data is appended to data
.
*
* - Preconditions:
-
*
*/
protected void scanPCData(StringBuffer data)
throws IOException {
for (;;) {
char ch = this.readChar();
if (ch == '<') {
ch = this.readChar();
if (ch == '!') {
this.checkCDATA(data);
} else {
this.unreadChar(ch);
return;
}
} else if (ch == '&') {
this.resolveEntity(data);
} else {
data.append(ch);
}
}
}
/**
* Scans a special tag and if the tag is a CDATA section, append its
* content to buf
.
*
* - Preconditions:
-
*
buf != null
* - The first < has already been read.
*
*/
protected boolean checkCDATA(StringBuffer buf)
throws IOException {
char ch = this.readChar();
if (ch != '[') {
this.unreadChar(ch);
this.skipSpecialTag(0);
return false;
} else if (!this.checkLiteral("CDATA[")) {
this.skipSpecialTag(1); // one [ has already been read
return false;
} else {
int delimiterCharsSkipped = 0;
while (delimiterCharsSkipped < 3) {
ch = this.readChar();
switch (ch) {
case ']':
if (delimiterCharsSkipped < 2) {
delimiterCharsSkipped += 1;
} else {
buf.append(']');
buf.append(']');
delimiterCharsSkipped = 0;
}
break;
case '>':
if (delimiterCharsSkipped < 2) {
for (int i = 0; i < delimiterCharsSkipped; i++) {
buf.append(']');
}
delimiterCharsSkipped = 0;
buf.append('>');
} else {
delimiterCharsSkipped = 3;
}
break;
default:
for (int i = 0; i < delimiterCharsSkipped; i += 1) {
buf.append(']');
}
buf.append(ch);
delimiterCharsSkipped = 0;
}
}
return true;
}
}
/**
* Skips a comment.
*
* - Preconditions:
-
*
- The first <!-- has already been read.
*
*/
protected void skipComment()
throws IOException {
int dashesToRead = 2;
while (dashesToRead > 0) {
char ch = this.readChar();
if (ch == '-') {
dashesToRead -= 1;
} else {
dashesToRead = 2;
}
// Be more tolerant of extra -- (double dashes)
// in comments.
if (dashesToRead == 0) {
ch = this.readChar();
if (ch == '>') {
return;
} else {
dashesToRead = 2;
this.unreadChar(ch);
}
}
}
/*
if (this.readChar() != '>') {
throw this.expectedInput(">");
}
*/
}
/**
* Skips a special tag or comment.
*
* @param bracketLevel The number of open square brackets ([) that have
* already been read.
*
* - Preconditions:
-
*
- The first <! has already been read.
*
bracketLevel >= 0
*
*/
protected void skipSpecialTag(int bracketLevel)
throws IOException {
int tagLevel = 1; // <
char stringDelimiter = '\0';
if (bracketLevel == 0) {
char ch = this.readChar();
if (ch == '[') {
bracketLevel += 1;
} else if (ch == '-') {
ch = this.readChar();
if (ch == '[') {
bracketLevel += 1;
} else if (ch == ']') {
bracketLevel -= 1;
} else if (ch == '-') {
this.skipComment();
return;
}
}
}
while (tagLevel > 0) {
char ch = this.readChar();
if (stringDelimiter == '\0') {
if ((ch == '"') || (ch == '\'')) {
stringDelimiter = ch;
} else if (bracketLevel <= 0) {
if (ch == '<') {
tagLevel += 1;
} else if (ch == '>') {
tagLevel -= 1;
}
}
if (ch == '[') {
bracketLevel += 1;
} else if (ch == ']') {
bracketLevel -= 1;
}
} else {
if (ch == stringDelimiter) {
stringDelimiter = '\0';
}
}
}
}
/**
* Scans the data for literal text.
* Scanning stops when a character does not match or after the complete
* text has been checked, whichever comes first.
*
* @param literal the literal to check.
*
* - Preconditions:
-
*
*/
protected boolean checkLiteral(String literal)
throws IOException {
int length = literal.length();
for (int i = 0; i < length; i += 1) {
if (this.readChar() != literal.charAt(i)) {
return false;
}
}
return true;
}
/**
* Reads a character from a reader.
*/
protected char readChar()
throws IOException {
if (this.charReadTooMuch != '\0') {
char ch = this.charReadTooMuch;
this.charReadTooMuch = '\0';
return ch;
} else {
int i = this.reader.read();
if (i < 0) {
throw this.unexpectedEndOfData();
} else if (i == 10) {
this.parserLineNr += 1;
return '\n';
} else {
return (char) i;
}
}
}
/**
* Scans an XML element.
*
* @param elt The element that will contain the result.
*
* - Preconditions:
-
*
- The first < has already been read.
*
elt != null
*
*/
protected void scanElement(XMLElement elt)
throws IOException {
StringBuffer buf = new StringBuffer();
this.scanIdentifier(buf);
String name = buf.toString();
elt.setName(name);
char ch = this.scanWhitespace();
while ((ch != '>') && (ch != '/')) {
buf.setLength(0);
this.unreadChar(ch);
this.scanIdentifier(buf);
String key = buf.toString();
ch = this.scanWhitespace();
if (ch != '=') {
throw this.expectedInput("=");
}
this.unreadChar(this.scanWhitespace());
buf.setLength(0);
this.scanString(buf);
elt.setAttribute(key, buf);
ch = this.scanWhitespace();
}
if (ch == '/') {
ch = this.readChar();
if (ch != '>') {
throw this.expectedInput(">");
}
return;
}
buf.setLength(0);
ch = this.scanWhitespace(buf);
if (ch != '<') {
this.unreadChar(ch);
this.scanPCData(buf);
} else {
for (;;) {
ch = this.readChar();
if (ch == '!') {
if (this.checkCDATA(buf)) {
this.scanPCData(buf);
break;
} else {
ch = this.scanWhitespace(buf);
if (ch != '<') {
this.unreadChar(ch);
this.scanPCData(buf);
break;
}
}
} else {
buf.setLength(0);
break;
}
}
}
if (buf.length() == 0) {
while (ch != '/') {
if (ch == '!') {
ch = this.readChar();
if (ch != '-') {
throw this.expectedInput("Comment or Element");
}
ch = this.readChar();
if (ch != '-') {
throw this.expectedInput("Comment or Element");
}
this.skipComment();
} else {
this.unreadChar(ch);
XMLElement child = this.createAnotherElement();
this.scanElement(child);
elt.addChild(child);
}
ch = this.scanWhitespace();
if (ch != '<') {
throw this.expectedInput("<");
}
ch = this.readChar();
}
this.unreadChar(ch);
} else {
if (this.ignoreWhitespace) {
elt.setContent(buf.toString().trim());
} else {
elt.setContent(buf.toString());
}
}
ch = this.readChar();
if (ch != '/') {
throw this.expectedInput("/");
}
this.unreadChar(this.scanWhitespace());
if (!this.checkLiteral(name)) {
throw this.expectedInput(name);
}
if (this.scanWhitespace() != '>') {
throw this.expectedInput(">");
}
}
/**
* Resolves an entity. The name of the entity is read from the reader.
* The value of the entity is appended to buf
.
*
* @param buf Where to put the entity value.
*
* - Preconditions:
-
*
- The first & has already been read.
*
buf != null
*
*/
protected void resolveEntity(StringBuffer buf)
throws IOException {
char ch = '\0';
StringBuffer keyBuf = new StringBuffer();
for (;;) {
ch = this.readChar();
if (ch == ';') {
break;
}
keyBuf.append(ch);
}
String key = keyBuf.toString();
if (key.charAt(0) == '#') {
try {
if (key.charAt(1) == 'x') {
ch = (char) Integer.parseInt(key.substring(2), 16);
} else {
ch = (char) Integer.parseInt(key.substring(1), 10);
}
} catch (NumberFormatException e) {
throw this.unknownEntity(key);
}
buf.append(ch);
} else {
char[] value = entities.get(key);
if (value == null) {
throw this.unknownEntity(key);
}
buf.append(value);
}
}
/**
* Pushes a character back to the read-back buffer.
*
* @param ch The character to push back.
*
* - Preconditions:
-
*
- The read-back buffer is empty.
*
ch != '\0'
*
*/
protected void unreadChar(char ch) {
this.charReadTooMuch = ch;
}
/**
* Creates a parse exception for when an invalid valueset is given to
* a method.
*
* @param name The name of the entity.
*
* - Preconditions:
-
*
*/
protected XMLParseException invalidValueSet(String name) {
String msg = "Invalid value set (entity name = \"" + name + "\")";
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when an invalid value is given to a
* method.
*
* @param name The name of the entity.
* @param value The value of the entity.
*
* - Preconditions:
-
*
name != null
* value != null
*
*/
protected XMLParseException invalidValue(String name,
String value) {
String msg = "Attribute \"" + name + "\" does not contain a valid "
+ "value (\"" + value + "\")";
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when the end of the data input has been
* reached.
*/
protected XMLParseException unexpectedEndOfData() {
String msg = "Unexpected end of data reached";
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when a syntax error occured.
*
* @param context The context in which the error occured.
*
* - Preconditions:
-
*
context != null
* context.length() > 0
*
*/
protected XMLParseException syntaxError(String context) {
String msg = "Syntax error while parsing " + context;
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when the next character read is not
* the character that was expected.
*
* @param charSet The set of characters (in human readable form) that was
* expected.
*
* - Preconditions:
-
*
charSet != null
* charSet.length() > 0
*
*/
protected XMLParseException expectedInput(String charSet) {
String msg = "Expected: " + charSet;
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when the next character read is not
* the character that was expected.
*
* @param charSet The set of characters (in human readable form) that was
* expected.
* @param ch The character that was received instead.
* - Preconditions:
-
*
charSet != null
* charSet.length() > 0
*
*/
protected XMLParseException expectedInput(String charSet, char ch) {
String msg = "Expected: '" + charSet + "'" + " but got: '" + ch + "'";
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Creates a parse exception for when an entity could not be resolved.
*
* @param name The name of the entity.
*
* - Preconditions:
-
*
name != null
* name.length() > 0
*
*/
protected XMLParseException unknownEntity(String name) {
String msg = "Unknown or invalid entity: &" + name + ";";
return new XMLParseException(this.getName(), this.parserLineNr, msg);
}
/**
* Reads an xml file and removes the comments, leaving only relevant
* xml code.
*
* @param isr The reader of the InputStream containing the xml.
* @param pout The PipedOutputStream that will be receiving the filtered
* xml file.
*/
public void sanitizeInput(Reader isr, OutputStream pout) {
try {
PrintStream out = new PrintStream(pout);
this.sanitizeCharReadTooMuch = '\0';
this.reader = isr;
this.parserLineNr = 0;
int newline = 2;
char prev = ' ';
while (true) {
char ch;
if (this.sanitizeCharReadTooMuch != '\0') {
ch = this.sanitizeCharReadTooMuch;
this.sanitizeCharReadTooMuch = '\0';
} else {
int i = this.reader.read();
if (i == -1) {
// no character in buffer, and nothing read
out.flush();
break;
} else if (i == 10) {
ch = '\n';
} else {
ch = (char) i;
}
}
char next;
int i = this.reader.read();
if (i == -1) {
// character in buffer and nothing read. write out
// what's in the buffer
out.print(ch);
out.flush();
if (JNLPRuntime.isDebug()) {
if (ch == 10) {
OutputController.getLogger().printOutLn("");
OutputController.getLogger().printOut("line: " + newline + " ");
newline++;
} else {
OutputController.getLogger().printOut(ch+"");
}
}
break;
} else if (i == 10) {
next = '\n';
} else {
next = (char) i;
}
this.sanitizeCharReadTooMuch = next;
// If the next chars are !--, then we've hit a comment tag,
// and should skip it.
if (ch == '<' && sanitizeCharReadTooMuch == '!') {
ch = (char) this.reader.read();
if (ch == '-') {
ch = (char) this.reader.read();
if (ch == '-') {
this.skipComment();
this.sanitizeCharReadTooMuch = '\0';
} else {
out.print('<');
out.print('!');
out.print('-');
this.sanitizeCharReadTooMuch = ch;
if (JNLPRuntime.isDebug()) {
OutputController.getLogger().printOut("<");
OutputController.getLogger().printOut("!");
OutputController.getLogger().printOut("-");
}
}
} else {
out.print('<');
out.print('!');
this.sanitizeCharReadTooMuch = ch;
if (JNLPRuntime.isDebug()) {
OutputController.getLogger().printOut("<");
OutputController.getLogger().printOut("!");
}
}
}
// Otherwise we haven't hit a comment, and we should write ch.
else {
out.print(ch);
if (JNLPRuntime.isDebug()) {
if (ch == 10) {
OutputController.getLogger().printOutLn("");
OutputController.getLogger().printOut("line: " + newline + " ");
newline++;
} else {
OutputController.getLogger().printOut(ch+"");
}
}
}
prev = next;
}
out.close();
isr.close();
} catch (Exception e) {
// Print the stack trace here -- xml.parseFromReader() will
// throw the ParseException if something goes wrong.
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
}
}
}