com.jidesoft.plaf.aqua.XMLElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jide-oss Show documentation
Show all versions of jide-oss Show documentation
JIDE Common Layer (Professional Swing Components)
/* XMLElement.java
*
* $Revision: 1.4 $
* $Date: 2002/03/24 10:27:59 $
* $Name: RELEASE_2_2_1 $
*
* 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.
*****************************************************************************/
package com.jidesoft.plaf.aqua;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* 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:
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
* #getStringAttribute(String) getStringAttribute}. The following example shows how to list the attributes of an
* element:
XMLElement element = ...;
Iterator iter = element.getAttributeNames();
while
* (iter.hasNext()) {
String key = (String) iter.next();
String
* value = element.getStringAttribute(key);
System.out.println(key + " = " + value);
}
*
- Retrieving Child Elements
- You can enumerate the children of an
* element using {@link #iterateChildren() iterateChildren}. The number of child iterator can be retrieved using {@link
* #countChildren() countChildren}.
- Elements Containing Character Data
- If an
* iterator contains character data, like in the following example:
<title>The Title</title>
*
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.
*
*
* @author Marc De Scheemaecker <[email protected]>
* @version 2005-06-18 Werner Randelshofer: Adapted for Java 2 Collections API.
$Name: RELEASE_2_2_1 $, $Revision:
* 1.4 $
* @see XMLParseException
*/
@SuppressWarnings({"CommentsWithMistakes"})
class XMLElement {
/**
* Serialization serial version ID.
*/
static final long serialVersionUID = 6685035139346394777L;
/**
* Major version of NanoXML. Classes with the same major and minor version are binary compatible. Classes with the
* same major version are source compatible. If the major version is different, you may need to modify the client
* source code.
*
* @see XMLElement#NANOXML_MINOR_VERSION
*/
public static final int NANOXML_MAJOR_VERSION = 2;
/**
* Minor version of NanoXML. Classes with the same major and minor version are binary compatible. Classes with the
* same major version are source compatible. If the major version is different, you may need to modify the client
* source code.
*
* @see XMLElement#NANOXML_MAJOR_VERSION
*/
public static final int NANOXML_MINOR_VERSION = 2;
/**
* The attributes given to the element.
*
* - Invariants:
-
- The field can be empty.
- The field is never
null
.
* - The keySet().iterator and the values are strings.
*/
private HashMap attributes;
/**
* Child iterator of the element.
*
* - Invariants:
-
- The field can be empty.
- The field is never
null
.
* - The iterator are instances of
XMLElement
or a subclass of XMLElement
.
*
*/
private ArrayList 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 keySet().iterator 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 keySet().iterator
* are strings
- The values are char arrays
*/
private HashMap entities;
/**
* The line number where the element starts.
*
* - Invariants:
-
lineNr >= 0
*/
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;
/**
* 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 HashMap(), false, true)
*
* - Postconditions:
-
- countChildren() => 0
- iterateChildren() => empty enumeration
*
- enumeratePropertyNames() => empty enumeration
- getChildren() => empty vector
- getContent() => ""
*
- getLineNr() => 0
- getName() => null
*
* @see XMLElement#XMLElement(java.util.HashMap) XMLElement(HashMap)
* @see XMLElement#XMLElement(boolean)
* @see XMLElement#XMLElement(java.util.HashMap,boolean) XMLElement(HashMap, boolean)
*/
public XMLElement() {
this(new HashMap(), false, true, true);
}
/**
* Creates and initializes a new XML element. Calling the construction is equivalent to: new
* XMLElement(entities, false, true)
*
* @param entities The entity conversion table.
*
* - Preconditions:
-
entities != null
*
*
* - Postconditions:
-
- countChildren() => 0
- iterateChildren() =>
* empty enumeration
- enumeratePropertyNames() => empty enumeration
- getChildren() => empty
* vector
- getContent() => ""
- getLineNr() => 0
- getName() => null
* @see XMLElement#XMLElement()
* @see XMLElement#XMLElement(boolean)
* @see XMLElement#XMLElement(java.util.HashMap,boolean) XMLElement(HashMap, boolean)
*/
public XMLElement(HashMap entities) {
this(entities, false, true, true);
}
/**
* Creates and initializes a new XML element. Calling the construction is equivalent to: new
* XMLElement(new HashMap(), skipLeadingWhitespace, true)
*
* @param skipLeadingWhitespace true
if leading and trailing whitespace in PCDATA content has to be
* removed.
*
*
- Postconditions:
-
- countChildren() => 0
*
- iterateChildren() => empty enumeration
- enumeratePropertyNames() => empty
* enumeration
- getChildren() => empty vector
- getContent() => ""
- getLineNr()
* => 0
- getName() => null
* @see XMLElement#XMLElement()
* @see XMLElement#XMLElement(java.util.HashMap) XMLElement(HashMap)
* @see XMLElement#XMLElement(java.util.HashMap,boolean) XMLElement(HashMap, boolean)
*/
public XMLElement(boolean skipLeadingWhitespace) {
this(new HashMap(), skipLeadingWhitespace, true, true);
}
/**
* Creates and initializes a new XML element. Calling the construction is equivalent to: new
* XMLElement(entities, skipLeadingWhitespace, true)
*
* @param entities The entity conversion table.
* @param skipLeadingWhitespace true
if leading and trailing whitespace in PCDATA content has to be
* removed.
*
*
- Preconditions:
-
entities != null
*
*
* - Postconditions:
-
- countChildren() => 0
*
- iterateChildren() => empty enumeration
- enumeratePropertyNames() => empty
* enumeration
- getChildren() => empty vector
- getContent() => ""
- getLineNr()
* => 0
- getName() => null
* @see XMLElement#XMLElement()
* @see XMLElement#XMLElement(boolean)
* @see XMLElement#XMLElement(java.util.HashMap) XMLElement(HashMap)
*/
public XMLElement(HashMap entities,
boolean skipLeadingWhitespace) {
this(entities, skipLeadingWhitespace, true, true);
}
/**
* Creates and initializes a new XML element.
*
* @param entities The entity conversion table.
* @param skipLeadingWhitespace true
if leading and trailing whitespace in PCDATA content has to be
* removed.
* @param ignoreCase true
if the case of element and attribute names have to be ignored.
*
*
- Preconditions:
-
entities != null
*
*
* - Postconditions:
-
- countChildren() => 0
*
- iterateChildren() => empty enumeration
- enumeratePropertyNames() => empty
* enumeration
- getChildren() => empty vector
- getContent() => ""
- getLineNr()
* => 0
- getName() => null
* @see XMLElement#XMLElement()
* @see XMLElement#XMLElement(boolean)
* @see XMLElement#XMLElement(java.util.HashMap) XMLElement(HashMap)
* @see XMLElement#XMLElement(java.util.HashMap,boolean) XMLElement(HashMap, boolean)
*/
public XMLElement(HashMap entities,
boolean skipLeadingWhitespace,
boolean ignoreCase) {
this(entities, skipLeadingWhitespace, true, ignoreCase);
}
/**
* Creates and initializes a new XML element.
*
* This constructor should only be called from {@link #createAnotherElement() createAnotherElement} to create
* child iterator.
*
* @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.
* @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
*
- iterateChildren() => empty enumeration
- enumeratePropertyNames() => empty
* enumeration
- getChildren() => empty vector
- getContent() => ""
*
- getLineNr() => 0
- getName() => null
* @see XMLElement#createAnotherElement()
*/
protected XMLElement(HashMap entities,
boolean skipLeadingWhitespace,
boolean fillBasicConversionTable,
boolean ignoreCase) {
this.ignoreWhitespace = skipLeadingWhitespace;
this.ignoreCase = ignoreCase;
this.name = null;
this.contents = "";
this.attributes = new HashMap();
this.children = new ArrayList();
this.entities = entities;
this.lineNr = 0;
Iterator iter = this.entities.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object value = this.entities.get(key);
if (value instanceof String) {
value = ((String) value).toCharArray();
this.entities.put(key, value);
}
}
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
*
- iterateChildren() => old.iterateChildren() + child
- getChildren() => old.iterateChildren() +
* child
* @see XMLElement#countChildren()
* @see XMLElement#iterateChildren()
* @see XMLElement#getChildren()
* @see XMLElement#removeChild(XMLElement) removeChild(XMLElement)
*/
public void addChild(XMLElement child) {
this.children.add(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
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getAttribute(String) getAttribute(String)
* @see XMLElement#getAttribute(String,Object) getAttribute(String, Object)
* @see XMLElement#getAttribute(String, java.util.HashMap, String,boolean) getAttribute(String, HashMap, String,
* boolean)
* @see XMLElement#getStringAttribute(String) getStringAttribute(String)
* @see XMLElement#getStringAttribute(String, String) getStringAttribute(String, String)
* @see XMLElement#getStringAttribute(String, java.util.HashMap, String,boolean) getStringAttribute(String, HashMap,
* String, boolean)
*/
public void setAttribute(String name,
Object value) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
this.attributes.put(name, value.toString());
}
/**
* Adds or modifies an attribute.
*
* @param name The name of the attribute.
* @param value The value of the attribute.
* @deprecated Use {@link #setAttribute(String,Object) setAttribute} instead.
*/
public void addProperty(String name,
Object value) {
this.setAttribute(name, value);
}
/**
* 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
*
* - Postconditions:
-
- enumerateAttributeNames() =>
* old.enumerateAttributeNames() + name
- getIntAttribute(name) => value
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getIntAttribute(String) getIntAttribute(String)
* @see XMLElement#getIntAttribute(String,int) getIntAttribute(String, int)
* @see XMLElement#getIntAttribute(String, java.util.HashMap, String,boolean) getIntAttribute(String, HashMap,
* String, boolean)
*/
public void setIntAttribute(String name,
int value) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
this.attributes.put(name, Integer.toString(value));
}
/**
* Adds or modifies an attribute.
*
* @param key The name of the attribute.
* @param value The value of the attribute.
* @deprecated Use {@link #setIntAttribute(String,int) setIntAttribute} instead.
*/
public void addProperty(String key,
int value) {
this.setIntAttribute(key, value);
}
/**
* 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
*
* - Postconditions:
-
- enumerateAttributeNames() =>
* old.enumerateAttributeNames() + name
- getDoubleAttribute(name) => value
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getDoubleAttribute(String) getDoubleAttribute(String)
* @see XMLElement#getDoubleAttribute(String,double) getDoubleAttribute(String, double)
* @see XMLElement#getDoubleAttribute(String, java.util.HashMap, String,boolean) getDoubleAttribute(String, HashMap,
* String, boolean)
*/
public void setDoubleAttribute(String name,
double value) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
this.attributes.put(name, Double.toString(value));
}
/**
* Adds or modifies an attribute.
*
* @param name The name of the attribute.
* @param value The value of the attribute.
* @deprecated Use {@link #setDoubleAttribute(String,double) setDoubleAttribute} instead.
*/
public void addProperty(String name,
double value) {
this.setDoubleAttribute(name, value);
}
/**
* Returns the number of child iterator of the element.
*
* - Postconditions:
-
result >= 0
*
* @see XMLElement#addChild(XMLElement) addChild(XMLElement)
* @see XMLElement#iterateChildren()
* @see XMLElement#getChildren()
* @see XMLElement#removeChild(XMLElement) removeChild(XMLElement)
*/
public int countChildren() {
return this.children.size();
}
/**
* Enumerates the attribute names.
*
* - Postconditions:
-
result != null
*
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#getAttribute(String) getAttribute(String)
* @see XMLElement#getAttribute(String,Object) getAttribute(String, String)
* @see XMLElement#getAttribute(String, java.util.HashMap, String,boolean) getAttribute(String, HashMap, String,
* boolean)
* @see XMLElement#getStringAttribute(String) getStringAttribute(String)
* @see XMLElement#getStringAttribute(String, String) getStringAttribute(String, String)
* @see XMLElement#getStringAttribute(String, java.util.HashMap, String,boolean) getStringAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getIntAttribute(String) getIntAttribute(String)
* @see XMLElement#getIntAttribute(String,int) getIntAttribute(String, int)
* @see XMLElement#getIntAttribute(String, java.util.HashMap, String,boolean) getIntAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getDoubleAttribute(String) getDoubleAttribute(String)
* @see XMLElement#getDoubleAttribute(String,double) getDoubleAttribute(String, double)
* @see XMLElement#getDoubleAttribute(String, java.util.HashMap, String,boolean) getDoubleAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getBooleanAttribute(String, String, String,boolean) getBooleanAttribute(String, String, String,
* boolean)
*/
public Iterator enumerateAttributeNames() {
return this.attributes.keySet().iterator();
}
/**
* Enumerates the attribute names.
*
* @deprecated Use {@link #enumerateAttributeNames() enumerateAttributeNames} instead.
*/
public Iterator enumeratePropertyNames() {
return this.enumerateAttributeNames();
}
/**
* Enumerates the child iterator.
*
* - Postconditions:
-
result != null
*
* @see XMLElement#addChild(XMLElement) addChild(XMLElement)
* @see XMLElement#countChildren()
* @see XMLElement#getChildren()
* @see XMLElement#removeChild(XMLElement) removeChild(XMLElement)
*/
public Iterator iterateChildren() {
return this.children.iterator();
}
/**
* Returns the child iterator as a ArrayList. It is safe to modify this ArrayList.
*
* - Postconditions:
-
result != null
*
* @see XMLElement#addChild(XMLElement) addChild(XMLElement)
* @see XMLElement#countChildren()
* @see XMLElement#iterateChildren()
* @see XMLElement#removeChild(XMLElement) removeChild(XMLElement)
*/
public ArrayList getChildren() {
try {
return (ArrayList) this.children.clone();
}
catch (Exception e) {
// this never happens, however, some Java compilers are so
// braindead that they require this exception clause
return null;
}
}
/**
* Returns the PCDATA content of the object. If there is no such content, null
is returned.
*
* @deprecated Use {@link #getContent() getContent} instead.
*/
public String getContents() {
return this.getContent();
}
/**
* Returns the PCDATA content of the object. If there is no such content, null
is returned.
*
* @see XMLElement#setContent(String) setContent(String)
*/
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:
-
result >= 0
*/
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
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getAttribute(String,Object) getAttribute(String, Object)
* @see XMLElement#getAttribute(String, java.util.HashMap, String,boolean) getAttribute(String, HashMap, String,
* boolean)
*/
public Object getAttribute(String name) {
return this.getAttribute(name, null);
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, defaultValue
is returned.
*
* @param name The name of the attribute.
* @param defaultValue Key to use if the attribute is missing.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getAttribute(String) getAttribute(String)
* @see XMLElement#getAttribute(String, java.util.HashMap, String,boolean) getAttribute(String, HashMap, String,
* boolean)
*/
public Object getAttribute(String name,
Object defaultValue) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object value = this.attributes.get(name);
if (value == null) {
value = defaultValue;
}
return value;
}
/**
* Returns an attribute by looking up a key in a hashtable. If the attribute doesn't exist, the value corresponding
* to defaultKey is returned.
*
* As an example, if valueSet contains the mapping "one" => "1"
and the element contains the attribute
* attr="one"
, then getAttribute("attr", mapping, defaultKey, false)
returns
* "1"
.
*
* @param name The name of the attribute.
* @param valueSet HashMap mapping keySet().iterator to values.
* @param defaultKey Key to use if the attribute is missing.
* @param allowLiterals true
if literals are valid.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier valueSet
!= null - the
* keySet().iterator of
valueSet
are strings
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getAttribute(String) getAttribute(String)
* @see XMLElement#getAttribute(String,Object) getAttribute(String, Object)
*/
public Object getAttribute(String name,
HashMap valueSet,
String defaultKey,
boolean allowLiterals) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object key = this.attributes.get(name);
Object result;
if (key == null) {
key = defaultKey;
}
result = valueSet.get(key);
if (result == null) {
if (allowLiterals) {
result = key;
}
else {
throw this.invalidValue(name, (String) key);
}
}
return result;
}
/**
* 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
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getStringAttribute(String, String) getStringAttribute(String, String)
* @see XMLElement#getStringAttribute(String, java.util.HashMap, String,boolean) getStringAttribute(String, HashMap,
* String, boolean)
*/
public String getStringAttribute(String name) {
return this.getStringAttribute(name, null);
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, defaultValue
is returned.
*
* @param name The name of the attribute.
* @param defaultValue Key to use if the attribute is missing.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getStringAttribute(String) getStringAttribute(String)
* @see XMLElement#getStringAttribute(String, java.util.HashMap, String,boolean) getStringAttribute(String, HashMap,
* String, boolean)
*/
public String getStringAttribute(String name,
String defaultValue) {
return (String) this.getAttribute(name, defaultValue);
}
/**
* Returns an attribute by looking up a key in a hashtable. If the attribute doesn't exist, the value corresponding
* to defaultKey is returned.
*
* As an example, if valueSet contains the mapping "one" => "1"
and the element contains the attribute
* attr="one"
, then getAttribute("attr", mapping, defaultKey, false)
returns
* "1"
.
*
* @param name The name of the attribute.
* @param valueSet HashMap mapping keySet().iterator to values.
* @param defaultKey Key to use if the attribute is missing.
* @param allowLiterals true
if literals are valid.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier valueSet
!= null - the
* keySet().iterator of
valueSet
are strings - the values of
*
valueSet
are strings
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getStringAttribute(String) getStringAttribute(String)
* @see XMLElement#getStringAttribute(String, String) getStringAttribute(String, String)
*/
public String getStringAttribute(String name,
HashMap valueSet,
String defaultKey,
boolean allowLiterals) {
return (String) this.getAttribute(name, valueSet, defaultKey,
allowLiterals);
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, 0
is returned.
*
* @param name The name of the attribute.
*
*
- Preconditions:
-
name != null
name
* is a valid XML identifier
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getIntAttribute(String,int) getIntAttribute(String, int)
* @see XMLElement#getIntAttribute(String, java.util.HashMap, String,boolean) getIntAttribute(String, HashMap,
* String, boolean)
*/
public int getIntAttribute(String name) {
return this.getIntAttribute(name, 0);
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, defaultValue
is returned.
*
* @param name The name of the attribute.
* @param defaultValue Key to use if the attribute is missing.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getIntAttribute(String) getIntAttribute(String)
* @see XMLElement#getIntAttribute(String, java.util.HashMap, String,boolean) getIntAttribute(String, HashMap,
* String, boolean)
*/
public int getIntAttribute(String name,
int defaultValue) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
String value = (String) this.attributes.get(name);
if (value == null) {
return defaultValue;
}
else {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
throw this.invalidValue(name, value);
}
}
}
/**
* Returns an attribute by looking up a key in a hashtable. If the attribute doesn't exist, the value corresponding
* to defaultKey is returned.
*
* As an example, if valueSet contains the mapping "one" => 1
and the element contains the attribute
* attr="one"
, then getIntAttribute("attr", mapping, defaultKey, false)
returns
* 1
.
*
* @param name The name of the attribute.
* @param valueSet HashMap mapping keySet().iterator to values.
* @param defaultKey Key to use if the attribute is missing.
* @param allowLiteralNumbers true
if literal numbers are valid.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier valueSet
!= null
* - the keySet().iterator of
valueSet
are strings - the values of
*
valueSet
are Integer objects defaultKey
is either
* null
, a key in valueSet
or an integer.
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getIntAttribute(String) getIntAttribute(String)
* @see XMLElement#getIntAttribute(String,int) getIntAttribute(String, int)
*/
public int getIntAttribute(String name,
HashMap valueSet,
String defaultKey,
boolean allowLiteralNumbers) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object key = this.attributes.get(name);
Integer result;
if (key == null) {
key = defaultKey;
}
try {
result = (Integer) valueSet.get(key);
}
catch (ClassCastException e) {
throw this.invalidValueSet(name);
}
if (result == null) {
if (!allowLiteralNumbers) {
throw this.invalidValue(name, (String) key);
}
try {
result = Integer.valueOf((String) key);
}
catch (NumberFormatException e) {
throw this.invalidValue(name, (String) key);
}
}
return result;
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, 0.0
is returned.
*
* @param name The name of the attribute.
*
*
- Preconditions:
-
name != null
name
* is a valid XML identifier
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getDoubleAttribute(String,double) getDoubleAttribute(String, double)
* @see XMLElement#getDoubleAttribute(String, java.util.HashMap, String,boolean) getDoubleAttribute(String, HashMap,
* String, boolean)
*/
public double getDoubleAttribute(String name) {
return this.getDoubleAttribute(name, 0.);
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, defaultValue
is returned.
*
* @param name The name of the attribute.
* @param defaultValue Key to use if the attribute is missing.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getDoubleAttribute(String) getDoubleAttribute(String)
* @see XMLElement#getDoubleAttribute(String, java.util.HashMap, String,boolean) getDoubleAttribute(String, HashMap,
* String, boolean)
*/
public double getDoubleAttribute(String name,
double defaultValue) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
String value = (String) this.attributes.get(name);
if (value == null) {
return defaultValue;
}
else {
try {
return Double.valueOf(value);
}
catch (NumberFormatException e) {
throw this.invalidValue(name, value);
}
}
}
/**
* Returns an attribute by looking up a key in a hashtable. If the attribute doesn't exist, the value corresponding
* to defaultKey is returned.
*
* As an example, if valueSet contains the mapping "one" => 1.0
and the element contains the
* attribute attr="one"
, then getDoubleAttribute("attr", mapping, defaultKey, false)
* returns 1.0
.
*
* @param name The name of the attribute.
* @param valueSet HashMap mapping keySet().iterator to values.
* @param defaultKey Key to use if the attribute is missing.
* @param allowLiteralNumbers true
if literal numbers are valid.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier valueSet != null
* - the keySet().iterator of
valueSet
are strings - the values of
*
valueSet
are Double objects defaultKey
is either
* null
, a key in valueSet
or a double.
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#getDoubleAttribute(String) getDoubleAttribute(String)
* @see XMLElement#getDoubleAttribute(String,double) getDoubleAttribute(String, double)
*/
public double getDoubleAttribute(String name,
HashMap valueSet,
String defaultKey,
boolean allowLiteralNumbers) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object key = this.attributes.get(name);
Double result;
if (key == null) {
key = defaultKey;
}
try {
result = (Double) valueSet.get(key);
}
catch (ClassCastException e) {
throw this.invalidValueSet(name);
}
if (result == null) {
if (!allowLiteralNumbers) {
throw this.invalidValue(name, (String) key);
}
try {
result = Double.valueOf((String) key);
}
catch (NumberFormatException e) {
throw this.invalidValue(name, (String) key);
}
}
return result;
}
/**
* Returns an attribute of the element. If the attribute doesn't exist, defaultValue
is returned. If
* the value of the attribute is equal to trueValue
, true
is returned. If the value of the
* attribute is equal to falseValue
, false
is returned. If the value doesn't match
* trueValue
or falseValue
, an exception is thrown.
*
* @param name The name of the attribute.
* @param trueValue The value associated with true
.
* @param falseValue The value associated with true
.
* @param defaultValue Value to use if the attribute is missing.
*
*
- Preconditions:
-
name != null
* name
is a valid XML identifier trueValue
and
* falseValue
are different strings.
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#removeAttribute(String) removeAttribute(String)
* @see XMLElement#enumerateAttributeNames()
*/
public boolean getBooleanAttribute(String name,
String trueValue,
String falseValue,
boolean defaultValue) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
Object value = this.attributes.get(name);
if (value == null) {
return defaultValue;
}
else if (value.equals(trueValue)) {
return true;
}
else if (value.equals(falseValue)) {
return false;
}
else {
throw this.invalidValue(name, (String) value);
}
}
/**
* Returns an attribute by looking up a key in a hashtable.
*
* @deprecated Use {@link #getIntAttribute(java.lang.String, java.util.HashMap,java.lang.String,boolean)
* getIntAttribute} instead.
*/
public int getIntProperty(String name,
HashMap valueSet,
String defaultKey) {
return this.getIntAttribute(name, valueSet, defaultKey, false);
}
/**
* Returns an attribute.
*
* @deprecated Use {@link #getStringAttribute(java.lang.String) getStringAttribute} instead.
*/
public String getProperty(String name) {
return this.getStringAttribute(name);
}
/**
* Returns an attribute.
*
* @deprecated Use {@link #getStringAttribute(java.lang.String, java.lang.String) getStringAttribute} instead.
*/
public String getProperty(String name,
String defaultValue) {
return this.getStringAttribute(name, defaultValue);
}
/**
* Returns an attribute.
*
* @deprecated Use {@link #getIntAttribute(java.lang.String,int) getIntAttribute} instead.
*/
public int getProperty(String name,
int defaultValue) {
return this.getIntAttribute(name, defaultValue);
}
/**
* Returns an attribute.
*
* @deprecated Use {@link #getDoubleAttribute(java.lang.String,double) getDoubleAttribute} instead.
*/
public double getProperty(String name,
double defaultValue) {
return this.getDoubleAttribute(name, defaultValue);
}
/**
* Returns an attribute.
*
* @deprecated Use {@link #getBooleanAttribute(java.lang.String, java.lang.String,java.lang.String,boolean)
* getBooleanAttribute} instead.
*/
public boolean getProperty(String key,
String trueValue,
String falseValue,
boolean defaultValue) {
return this.getBooleanAttribute(key, trueValue, falseValue,
defaultValue);
}
/**
* Returns an attribute by looking up a key in a hashtable.
*
* @deprecated Use {@link #getAttribute(java.lang.String, java.util.HashMap,java.lang.String,boolean) getAttribute}
* instead.
*/
public Object getProperty(String name,
HashMap valueSet,
String defaultKey) {
return this.getAttribute(name, valueSet, defaultKey, false);
}
/**
* Returns an attribute by looking up a key in a hashtable.
*
* @deprecated Use {@link #getStringAttribute(java.lang.String, java.util.HashMap,java.lang.String,boolean)
* getStringAttribute} instead.
*/
public String getStringProperty(String name,
HashMap valueSet,
String defaultKey) {
return this.getStringAttribute(name, valueSet, defaultKey, false);
}
/**
* Returns an attribute by looking up a key in a hashtable.
*
* @deprecated Use {@link #getIntAttribute(java.lang.String, java.util.HashMap,java.lang.String,boolean)
* getIntAttribute} instead.
*/
public int getSpecialIntProperty(String name,
HashMap valueSet,
String defaultKey) {
return this.getIntAttribute(name, valueSet, defaultKey, true);
}
/**
* Returns an attribute by looking up a key in a hashtable.
*
* @deprecated Use {@link #getDoubleAttribute(java.lang.String, java.util.HashMap,java.lang.String,boolean)
* getDoubleAttribute} instead.
*/
public double getSpecialDoubleProperty(String name,
HashMap valueSet,
String defaultKey) {
return this.getDoubleAttribute(name, valueSet, defaultKey, true);
}
/**
* Returns the name of the element.
*
* @see XMLElement#setName(java.lang.String) setName(String)
*/
public String getName() {
return this.name;
}
/**
* Returns the name of the element.
*
* @deprecated Use {@link #getName() getName} instead.
*/
public String getTagName() {
return this.getName();
}
/**
* 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 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 XMLParseException If an error occured while parsing the read data.
*/
public void parseFromReader(Reader reader,
int startingLineNr)
throws IOException, XMLParseException {
this.name = null;
this.contents = "";
this.attributes = new HashMap();
this.children = new ArrayList();
this.charReadTooMuch = '\0';
this.reader = reader;
this.parserLineNr = startingLineNr;
for (; ;) {
char ch = this.scanWhitespace();
if (ch != '<') {
throw this.expectedInput("<");
}
ch = this.readChar();
if ((ch == '!') || (ch == '?')) {
this.skipSpecialTag(0);
}
else {
this.unreadChar(ch);
this.scanElement(this);
return;
}
}
}
/**
* Reads one XML element from a String and parses it.
*
* @param string The reader from which to retrieve the XML data.
*
*
- Preconditions:
-
string != null
* string.length() > 0
*
* - Postconditions:
-
- the state of the receiver is updated to reflect the
* XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseString(String string)
throws XMLParseException {
try {
this.parseFromReader(new StringReader(string),
/*startingLineNr*/ 1);
}
catch (IOException e) {
// Java exception handling suxx
}
}
/**
* Reads one XML element from a String and parses it.
*
* @param string The reader from which to retrieve the XML data.
* @param offset The first character in string
to scan.
*
*
- Preconditions:
-
string != null
offset
* < string.length()
offset >= 0
*
* - Postconditions:
-
- the state of the receiver is updated to reflect the
* XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseString(String string,
int offset)
throws XMLParseException {
this.parseString(string.substring(offset));
}
/**
* Reads one XML element from a String and parses it.
*
* @param string The reader from which to retrieve the XML data.
* @param offset The first character in string
to scan.
* @param end The character where to stop scanning. This character is not scanned.
*
*
- Preconditions:
-
string != null
end <=
* string.length()
offset < end
offset >= 0
*
*
* - Postconditions:
-
- the state of the receiver is updated to reflect the
* XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseString(String string,
int offset,
int end)
throws XMLParseException {
this.parseString(string.substring(offset, end));
}
/**
* Reads one XML element from a String and parses it.
*
* @param string The reader from which to retrieve the XML data.
* @param offset The first character in string
to scan.
* @param end The character where to stop scanning. This character is not scanned.
* @param startingLineNr The line number of the first line in the data.
*
*
- Preconditions:
-
string != null
* end <= string.length()
offset < end
* offset >= 0
*
* - Postconditions:
-
- the state of the receiver is updated to
* reflect the XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseString(String string,
int offset,
int end,
int startingLineNr)
throws XMLParseException {
string = string.substring(offset, end);
try {
this.parseFromReader(new StringReader(string), startingLineNr);
}
catch (IOException e) {
// Java exception handling suxx
}
}
/**
* Reads one XML element from a char array and parses it.
*
* @param input The reader from which to retrieve the XML data.
* @param offset The first character in string
to scan.
* @param end The character where to stop scanning. This character is not scanned.
*
*
- Preconditions:
-
input != null
end <=
* input.length
offset < end
offset >= 0
*
*
* - Postconditions:
-
- the state of the receiver is updated to reflect the
* XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseCharArray(char[] input,
int offset,
int end)
throws XMLParseException {
this.parseCharArray(input, offset, end, /*startingLineNr*/ 1);
}
/**
* Reads one XML element from a char array and parses it.
*
* @param input The reader from which to retrieve the XML data.
* @param offset The first character in string
to scan.
* @param end The character where to stop scanning. This character is not scanned.
* @param startingLineNr The line number of the first line in the data.
*
*
- Preconditions:
-
input != null
* end <= input.length
offset < end
offset
* >= 0
*
* - Postconditions:
-
- the state of the receiver is updated to
* reflect the XML element parsed from the reader
* @throws XMLParseException If an error occured while parsing the string.
*/
public void parseCharArray(char[] input,
int offset,
int end,
int startingLineNr)
throws XMLParseException {
try {
Reader reader = new CharArrayReader(input, offset, end);
this.parseFromReader(reader, startingLineNr);
}
catch (IOException e) {
// This exception will never happen.
}
}
/**
* Removes a child element.
*
* @param child The child element to remove.
*
*
- Preconditions:
-
child != null
* child
is a child element of the receiver
*
* - Postconditions:
-
- countChildren() => old.countChildren() - 1
*
- iterateChildren() => old.iterateChildren() - child
- getChildren() => old.iterateChildren() -
* child
* @see XMLElement#addChild(XMLElement) addChild(XMLElement)
* @see XMLElement#countChildren()
* @see XMLElement#iterateChildren()
* @see XMLElement#getChildren()
*/
public void removeChild(XMLElement child) {
this.children.remove(child);
}
/**
* Removes an attribute.
*
* @param name The name of the attribute.
*
*
- Preconditions:
-
name != null
name
* is a valid XML identifier
*
* - Postconditions:
-
- enumerateAttributeNames() =>
* old.enumerateAttributeNames() - name
- getAttribute(name) =>
null
* @see XMLElement#enumerateAttributeNames()
* @see XMLElement#setDoubleAttribute(String,double) setDoubleAttribute(String, double)
* @see XMLElement#setIntAttribute(String,int) setIntAttribute(String, int)
* @see XMLElement#setAttribute(String,Object) setAttribute(String, Object)
* @see XMLElement#getAttribute(String) getAttribute(String)
* @see XMLElement#getAttribute(String,Object) getAttribute(String, Object)
* @see XMLElement#getAttribute(String, java.util.HashMap, String,boolean) getAttribute(String, HashMap, String,
* boolean)
* @see XMLElement#getStringAttribute(String) getStringAttribute(String)
* @see XMLElement#getStringAttribute(String, String) getStringAttribute(String, String)
* @see XMLElement#getStringAttribute(String, java.util.HashMap, String,boolean) getStringAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getIntAttribute(String) getIntAttribute(String)
* @see XMLElement#getIntAttribute(String,int) getIntAttribute(String, int)
* @see XMLElement#getIntAttribute(String, java.util.HashMap, String,boolean) getIntAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getDoubleAttribute(String) getDoubleAttribute(String)
* @see XMLElement#getDoubleAttribute(String,double) getDoubleAttribute(String, double)
* @see XMLElement#getDoubleAttribute(String, java.util.HashMap, String,boolean) getDoubleAttribute(String, HashMap,
* String, boolean)
* @see XMLElement#getBooleanAttribute(String, String, String,boolean) getBooleanAttribute(String, String, String,
* boolean)
*/
public void removeAttribute(String name) {
if (this.ignoreCase) {
name = name.toUpperCase();
}
this.attributes.remove(name);
}
/**
* Removes an attribute.
*
* @param name The name of the attribute.
* @deprecated Use {@link #removeAttribute(String) removeAttribute} instead.
*/
public void removeProperty(String name) {
this.removeAttribute(name);
}
/**
* Removes an attribute.
*
* @param name The name of the attribute.
* @deprecated Use {@link #removeAttribute(String) removeAttribute} instead.
*/
public void removeChild(String name) {
this.removeAttribute(name);
}
/**
* Creates a new similar XML element.
*
* You should override this method when subclassing XMLElement.
*/
public 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.
* @deprecated Use {@link #setName(String) setName} instead.
*/
public void setTagName(String name) {
this.setName(name);
}
/**
* Changes the name of the element.
*
* @param name The new name.
*
*
- Preconditions:
-
name != null
name
* is a valid XML identifier
* @see XMLElement#getName()
*/
public void setName(String name) {
this.name = name;
}
/**
* Writes the XML element to a string.
*
* @see XMLElement#write(java.io.Writer) write(Writer)
*/
@Override
public String toString() {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(out);
this.write(writer);
writer.flush();
return new String(out.toByteArray());
}
catch (IOException e) {
// Java exception handling suxx
return super.toString();
}
}
/**
* Writes the XML element to a writer.
*
* @param writer The writer to write the XML data to.
*
* - Preconditions:
-
writer != null
* writer
is not closed
* @throws java.io.IOException If the data could not be written to the writer.
* @see XMLElement#toString()
*/
public void write(Writer writer)
throws IOException {
if (this.name == null) {
this.writeEncoded(writer, this.contents);
return;
}
writer.write('<');
writer.write(this.name);
if (!this.attributes.isEmpty()) {
Iterator iter = this.attributes.keySet().iterator();
while (iter.hasNext()) {
writer.write(' ');
String key = (String) iter.next();
String value = (String) this.attributes.get(key);
writer.write(key);
writer.write('=');
writer.write('"');
this.writeEncoded(writer, value);
writer.write('"');
}
}
if ((this.contents != null) && (this.contents.length() > 0)) {
writer.write('>');
this.writeEncoded(writer, this.contents);
writer.write('<');
writer.write('/');
writer.write(this.name);
writer.write('>');
}
else if (this.children.isEmpty()) {
writer.write('/');
writer.write('>');
}
else {
writer.write('>');
Iterator iter = this.iterateChildren();
while (iter.hasNext()) {
XMLElement child = (XMLElement) iter.next();
child.write(writer);
}
writer.write('<');
writer.write('/');
writer.write(this.name);
writer.write('>');
}
}
/**
* Writes a string encoded to a writer.
*
* @param writer The writer to write the XML data to.
* @param str The string to write encoded.
*
* - Preconditions:
-
writer != null
* writer
is not closed str != null
*/
protected void writeEncoded(Writer writer,
String str)
throws IOException {
for (int i = 0; i < str.length(); i += 1) {
char ch = str.charAt(i);
switch (ch) {
case '<':
writer.write('&');
writer.write('l');
writer.write('t');
writer.write(';');
break;
case '>':
writer.write('&');
writer.write('g');
writer.write('t');
writer.write(';');
break;
case '&':
writer.write('&');
writer.write('a');
writer.write('m');
writer.write('p');
writer.write(';');
break;
case '"':
writer.write('&');
writer.write('q');
writer.write('u');
writer.write('o');
writer.write('t');
writer.write(';');
break;
case '\'':
writer.write('&');
writer.write('a');
writer.write('p');
writer.write('o');
writer.write('s');
writer.write(';');
break;
default:
int unicode = (int) ch;
if ((unicode < 32) || (unicode > 126)) {
writer.write('&');
writer.write('#');
writer.write('x');
writer.write(Integer.toString(unicode, 16));
writer.write(';');
}
else {
writer.write(ch);
}
}
}
}
/**
* 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:
-
result != null
*/
protected char scanWhitespace(StringBuffer result)
throws IOException {
for (; ;) {
char ch = this.readChar();
switch (ch) {
case ' ':
case '\t':
case '\n':
result.append(ch);
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:
-
data != null
*/
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;
}
}
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:
-
literal != null
*/
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 {
if ((ch != '/') || this.ignoreWhitespace) {
buf.setLength(0);
}
if (ch == '/') {
this.unreadChar(ch);
}
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 = (char[]) this.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:
-
name != null
*/
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 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy