0001: /* XMLElement.java
0002: *
0003: * $Revision: 1.4 $
0004: * $Date: 2002/03/24 10:27:59 $
0005: * $Name: RELEASE_2_2_1 $
0006: *
0007: * This file is part of NanoXML 2 Lite.
0008: * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
0009: *
0010: * This software is provided 'as-is', without any express or implied warranty.
0011: * In no event will the authors be held liable for any damages arising from the
0012: * use of this software.
0013: *
0014: * Permission is granted to anyone to use this software for any purpose,
0015: * including commercial applications, and to alter it and redistribute it
0016: * freely, subject to the following restrictions:
0017: *
0018: * 1. The origin of this software must not be misrepresented; you must not
0019: * claim that you wrote the original software. If you use this software in
0020: * a product, an acknowledgment in the product documentation would be
0021: * appreciated but is not required.
0022: *
0023: * 2. Altered source versions must be plainly marked as such, and must not be
0024: * misrepresented as being the original software.
0025: *
0026: * 3. This notice may not be removed or altered from any source distribution.
0027: *****************************************************************************/
0028:
0029: package nanoxml;
0030:
0031: import java.io.ByteArrayOutputStream;
0032: import java.io.CharArrayReader;
0033: import java.io.IOException;
0034: import java.io.OutputStreamWriter;
0035: import java.io.Reader;
0036: import java.io.StringReader;
0037: import java.io.Writer;
0038: import java.util.Enumeration;
0039: import java.util.Hashtable;
0040: import java.util.Vector;
0041:
0042: /**
0043: * XMLElement is a representation of an XML object. The object is able to parse
0044: * XML code.
0045: * <P><DL>
0046: * <DT><B>Parsing XML Data</B></DT>
0047: * <DD>
0048: * You can parse XML data using the following code:
0049: * <UL><CODE>
0050: * XMLElement xml = new XMLElement();<BR>
0051: * FileReader reader = new FileReader("filename.xml");<BR>
0052: * xml.parseFromReader(reader);
0053: * </CODE></UL></DD></DL>
0054: * <DL><DT><B>Retrieving Attributes</B></DT>
0055: * <DD>
0056: * You can enumerate the attributes of an element using the method
0057: * {@link #enumerateAttributeNames() enumerateAttributeNames}.
0058: * The attribute values can be retrieved using the method
0059: * {@link #getStringAttribute(java.lang.String) getStringAttribute}.
0060: * The following example shows how to list the attributes of an element:
0061: * <UL><CODE>
0062: * XMLElement element = ...;<BR>
0063: * Enumeration enum = element.getAttributeNames();<BR>
0064: * while (enum.hasMoreElements()) {<BR>
0065: * String key = (String) enum.nextElement();<BR>
0066: * String value = element.getStringAttribute(key);<BR>
0067: * System.out.println(key + " = " + value);<BR>
0068: * }
0069: * </CODE></UL></DD></DL>
0070: * <DL><DT><B>Retrieving Child Elements</B></DT>
0071: * <DD>
0072: * You can enumerate the children of an element using
0073: * {@link #enumerateChildren() enumerateChildren}.
0074: * The number of child elements can be retrieved using
0075: * {@link #countChildren() countChildren}.
0076: * </DD></DL>
0077: * <DL><DT><B>Elements Containing Character Data</B></DT>
0078: * <DD>
0079: * If an elements contains character data, like in the following example:
0080: * <UL><CODE>
0081: * <title>The Title</title>
0082: * </CODE></UL>
0083: * you can retrieve that data using the method
0084: * {@link #getContent() getContent}.
0085: * </DD></DL>
0086: * <DL><DT><B>Subclassing XMLElement</B></DT>
0087: * <DD>
0088: * When subclassing XMLElement, you need to override the method
0089: * {@link #createAnotherElement() createAnotherElement}
0090: * which has to return a new copy of the receiver.
0091: * </DD></DL>
0092: * <P>
0093: *
0094: * @see nanoxml.XMLParseException
0095: *
0096: * @author Marc De Scheemaecker
0097: * <<A href="mailto:cyberelf@mac.com">cyberelf@mac.com</A>>
0098: * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
0099: */
0100: public class XMLElement {
0101:
0102: /**
0103: * Serialization serial version ID.
0104: */
0105: static final long serialVersionUID = 6685035139346394777L;
0106:
0107: /**
0108: * Major version of NanoXML. Classes with the same major and minor
0109: * version are binary compatible. Classes with the same major version
0110: * are source compatible. If the major version is different, you may
0111: * need to modify the client source code.
0112: *
0113: * @see nanoxml.XMLElement#NANOXML_MINOR_VERSION
0114: */
0115: public static final int NANOXML_MAJOR_VERSION = 2;
0116:
0117: /**
0118: * Minor version of NanoXML. Classes with the same major and minor
0119: * version are binary compatible. Classes with the same major version
0120: * are source compatible. If the major version is different, you may
0121: * need to modify the client source code.
0122: *
0123: * @see nanoxml.XMLElement#NANOXML_MAJOR_VERSION
0124: */
0125: public static final int NANOXML_MINOR_VERSION = 2;
0126:
0127: /**
0128: * The attributes given to the element.
0129: *
0130: * <dl><dt><b>Invariants:</b></dt><dd>
0131: * <ul><li>The field can be empty.
0132: * <li>The field is never <code>null</code>.
0133: * <li>The keys and the values are strings.
0134: * </ul></dd></dl>
0135: */
0136: private Hashtable attributes;
0137:
0138: /**
0139: * Child elements of the element.
0140: *
0141: * <dl><dt><b>Invariants:</b></dt><dd>
0142: * <ul><li>The field can be empty.
0143: * <li>The field is never <code>null</code>.
0144: * <li>The elements are instances of <code>XMLElement</code>
0145: * or a subclass of <code>XMLElement</code>.
0146: * </ul></dd></dl>
0147: */
0148: private Vector children;
0149:
0150: /**
0151: * The name of the element.
0152: *
0153: * <dl><dt><b>Invariants:</b></dt><dd>
0154: * <ul><li>The field is <code>null</code> iff the element is not
0155: * initialized by either parse or setName.
0156: * <li>If the field is not <code>null</code>, it's not empty.
0157: * <li>If the field is not <code>null</code>, it contains a valid
0158: * XML identifier.
0159: * </ul></dd></dl>
0160: */
0161: private String name;
0162:
0163: /**
0164: * The #PCDATA content of the object.
0165: *
0166: * <dl><dt><b>Invariants:</b></dt><dd>
0167: * <ul><li>The field is <code>null</code> iff the element is not a
0168: * #PCDATA element.
0169: * <li>The field can be any string, including the empty string.
0170: * </ul></dd></dl>
0171: */
0172: private String contents;
0173:
0174: /**
0175: * Conversion table for &...; entities. The keys are the entity names
0176: * without the & and ; delimiters.
0177: *
0178: * <dl><dt><b>Invariants:</b></dt><dd>
0179: * <ul><li>The field is never <code>null</code>.
0180: * <li>The field always contains the following associations:
0181: * "lt" => "<", "gt" => ">",
0182: * "quot" => "\"", "apos" => "'",
0183: * "amp" => "&"
0184: * <li>The keys are strings
0185: * <li>The values are char arrays
0186: * </ul></dd></dl>
0187: */
0188: private Hashtable entities;
0189:
0190: /**
0191: * The line number where the element starts.
0192: *
0193: * <dl><dt><b>Invariants:</b></dt><dd>
0194: * <ul><li><code>lineNr >= 0</code>
0195: * </ul></dd></dl>
0196: */
0197: private int lineNr;
0198:
0199: /**
0200: * <code>true</code> if the case of the element and attribute names
0201: * are case insensitive.
0202: */
0203: private boolean ignoreCase;
0204:
0205: /**
0206: * <code>true</code> if the leading and trailing whitespace of #PCDATA
0207: * sections have to be ignored.
0208: */
0209: private boolean ignoreWhitespace;
0210:
0211: /**
0212: * Character read too much.
0213: * This character provides push-back functionality to the input reader
0214: * without having to use a PushbackReader.
0215: * If there is no such character, this field is '\0'.
0216: */
0217: private char charReadTooMuch;
0218:
0219: /**
0220: * The reader provided by the caller of the parse method.
0221: *
0222: * <dl><dt><b>Invariants:</b></dt><dd>
0223: * <ul><li>The field is not <code>null</code> while the parse method
0224: * is running.
0225: * </ul></dd></dl>
0226: */
0227: private Reader reader;
0228:
0229: /**
0230: * The current line number in the source content.
0231: *
0232: * <dl><dt><b>Invariants:</b></dt><dd>
0233: * <ul><li>parserLineNr > 0 while the parse method is running.
0234: * </ul></dd></dl>
0235: */
0236: private int parserLineNr;
0237:
0238: /**
0239: * Creates and initializes a new XML element.
0240: * Calling the construction is equivalent to:
0241: * <ul><code>new XMLElement(new Hashtable(), false, true)
0242: * </code></ul>
0243: *
0244: * <dl><dt><b>Postconditions:</b></dt><dd>
0245: * <ul><li>countChildren() => 0
0246: * <li>enumerateChildren() => empty enumeration
0247: * <li>enumeratePropertyNames() => empty enumeration
0248: * <li>getChildren() => empty vector
0249: * <li>getContent() => ""
0250: * <li>getLineNr() => 0
0251: * <li>getName() => null
0252: * </ul></dd></dl>
0253: *
0254: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
0255: * XMLElement(Hashtable)
0256: * @see nanoxml.XMLElement#XMLElement(boolean)
0257: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
0258: * XMLElement(Hashtable, boolean)
0259: */
0260: public XMLElement() {
0261: this (new Hashtable(), false, true, true);
0262: }
0263:
0264: /**
0265: * Creates and initializes a new XML element.
0266: * Calling the construction is equivalent to:
0267: * <ul><code>new XMLElement(entities, false, true)
0268: * </code></ul>
0269: *
0270: * @param entities
0271: * The entity conversion table.
0272: *
0273: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0274: * <ul><li><code>entities != null</code>
0275: * </ul></dd></dl>
0276: *
0277: * <dl><dt><b>Postconditions:</b></dt><dd>
0278: * <ul><li>countChildren() => 0
0279: * <li>enumerateChildren() => empty enumeration
0280: * <li>enumeratePropertyNames() => empty enumeration
0281: * <li>getChildren() => empty vector
0282: * <li>getContent() => ""
0283: * <li>getLineNr() => 0
0284: * <li>getName() => null
0285: * </ul></dd></dl><dl>
0286: *
0287: * @see nanoxml.XMLElement#XMLElement()
0288: * @see nanoxml.XMLElement#XMLElement(boolean)
0289: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
0290: * XMLElement(Hashtable, boolean)
0291: */
0292: public XMLElement(Hashtable entities) {
0293: this (entities, false, true, true);
0294: }
0295:
0296: /**
0297: * Creates and initializes a new XML element.
0298: * Calling the construction is equivalent to:
0299: * <ul><code>new XMLElement(new Hashtable(), skipLeadingWhitespace, true)
0300: * </code></ul>
0301: *
0302: * @param skipLeadingWhitespace
0303: * <code>true</code> if leading and trailing whitespace in PCDATA
0304: * content has to be removed.
0305: *
0306: * </dl><dl><dt><b>Postconditions:</b></dt><dd>
0307: * <ul><li>countChildren() => 0
0308: * <li>enumerateChildren() => empty enumeration
0309: * <li>enumeratePropertyNames() => empty enumeration
0310: * <li>getChildren() => empty vector
0311: * <li>getContent() => ""
0312: * <li>getLineNr() => 0
0313: * <li>getName() => null
0314: * </ul></dd></dl><dl>
0315: *
0316: * @see nanoxml.XMLElement#XMLElement()
0317: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
0318: * XMLElement(Hashtable)
0319: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
0320: * XMLElement(Hashtable, boolean)
0321: */
0322: public XMLElement(boolean skipLeadingWhitespace) {
0323: this (new Hashtable(), skipLeadingWhitespace, true, true);
0324: }
0325:
0326: /**
0327: * Creates and initializes a new XML element.
0328: * Calling the construction is equivalent to:
0329: * <ul><code>new XMLElement(entities, skipLeadingWhitespace, true)
0330: * </code></ul>
0331: *
0332: * @param entities
0333: * The entity conversion table.
0334: * @param skipLeadingWhitespace
0335: * <code>true</code> if leading and trailing whitespace in PCDATA
0336: * content has to be removed.
0337: *
0338: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0339: * <ul><li><code>entities != null</code>
0340: * </ul></dd></dl>
0341: *
0342: * <dl><dt><b>Postconditions:</b></dt><dd>
0343: * <ul><li>countChildren() => 0
0344: * <li>enumerateChildren() => empty enumeration
0345: * <li>enumeratePropertyNames() => empty enumeration
0346: * <li>getChildren() => empty vector
0347: * <li>getContent() => ""
0348: * <li>getLineNr() => 0
0349: * <li>getName() => null
0350: * </ul></dd></dl><dl>
0351: *
0352: * @see nanoxml.XMLElement#XMLElement()
0353: * @see nanoxml.XMLElement#XMLElement(boolean)
0354: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
0355: * XMLElement(Hashtable)
0356: */
0357: public XMLElement(Hashtable entities, boolean skipLeadingWhitespace) {
0358: this (entities, skipLeadingWhitespace, true, true);
0359: }
0360:
0361: /**
0362: * Creates and initializes a new XML element.
0363: *
0364: * @param entities
0365: * The entity conversion table.
0366: * @param skipLeadingWhitespace
0367: * <code>true</code> if leading and trailing whitespace in PCDATA
0368: * content has to be removed.
0369: * @param ignoreCase
0370: * <code>true</code> if the case of element and attribute names have
0371: * to be ignored.
0372: *
0373: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0374: * <ul><li><code>entities != null</code>
0375: * </ul></dd></dl>
0376: *
0377: * <dl><dt><b>Postconditions:</b></dt><dd>
0378: * <ul><li>countChildren() => 0
0379: * <li>enumerateChildren() => empty enumeration
0380: * <li>enumeratePropertyNames() => empty enumeration
0381: * <li>getChildren() => empty vector
0382: * <li>getContent() => ""
0383: * <li>getLineNr() => 0
0384: * <li>getName() => null
0385: * </ul></dd></dl><dl>
0386: *
0387: * @see nanoxml.XMLElement#XMLElement()
0388: * @see nanoxml.XMLElement#XMLElement(boolean)
0389: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
0390: * XMLElement(Hashtable)
0391: * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
0392: * XMLElement(Hashtable, boolean)
0393: */
0394: public XMLElement(Hashtable entities,
0395: boolean skipLeadingWhitespace, boolean ignoreCase) {
0396: this (entities, skipLeadingWhitespace, true, ignoreCase);
0397: }
0398:
0399: /**
0400: * Creates and initializes a new XML element.
0401: * <P>
0402: * This constructor should <I>only</I> be called from
0403: * {@link #createAnotherElement() createAnotherElement}
0404: * to create child elements.
0405: *
0406: * @param entities
0407: * The entity conversion table.
0408: * @param skipLeadingWhitespace
0409: * <code>true</code> if leading and trailing whitespace in PCDATA
0410: * content has to be removed.
0411: * @param fillBasicConversionTable
0412: * <code>true</code> if the basic entities need to be added to
0413: * the entity list.
0414: * @param ignoreCase
0415: * <code>true</code> if the case of element and attribute names have
0416: * to be ignored.
0417: *
0418: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0419: * <ul><li><code>entities != null</code>
0420: * <li>if <code>fillBasicConversionTable == false</code>
0421: * then <code>entities</code> contains at least the following
0422: * entries: <code>amp</code>, <code>lt</code>, <code>gt</code>,
0423: * <code>apos</code> and <code>quot</code>
0424: * </ul></dd></dl>
0425: *
0426: * <dl><dt><b>Postconditions:</b></dt><dd>
0427: * <ul><li>countChildren() => 0
0428: * <li>enumerateChildren() => empty enumeration
0429: * <li>enumeratePropertyNames() => empty enumeration
0430: * <li>getChildren() => empty vector
0431: * <li>getContent() => ""
0432: * <li>getLineNr() => 0
0433: * <li>getName() => null
0434: * </ul></dd></dl><dl>
0435: *
0436: * @see nanoxml.XMLElement#createAnotherElement()
0437: */
0438: protected XMLElement(Hashtable entities,
0439: boolean skipLeadingWhitespace,
0440: boolean fillBasicConversionTable,
0441: boolean ignoreCase)
0442: {
0443: this .ignoreWhitespace = skipLeadingWhitespace;
0444: this .ignoreCase = ignoreCase;
0445: this .name = null;
0446: this .contents = "";
0447: this .attributes = new Hashtable();
0448: this .children = new Vector();
0449: this .entities = entities;
0450: this .lineNr = 0;
0451: Enumeration enum = this .entities.keys();
0452: while (enum.hasMoreElements()) {
0453: Object key = enum.nextElement();
0454: Object value = this .entities.get(key);
0455: if (value instanceof String) {
0456: value = ((String) value).toCharArray();
0457: this .entities.put(key, value);
0458: }
0459: }
0460: if (fillBasicConversionTable) {
0461: this .entities.put("amp", new char[] { '&' });
0462: this .entities.put("quot", new char[] { '"' });
0463: this .entities.put("apos", new char[] { '\'' });
0464: this .entities.put("lt", new char[] { '<' });
0465: this .entities.put("gt", new char[] { '>' });
0466: }
0467: }
0468:
0469: /**
0470: * Adds a child element.
0471: *
0472: * @param child
0473: * The child element to add.
0474: *
0475: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0476: * <ul><li><code>child != null</code>
0477: * <li><code>child.getName() != null</code>
0478: * <li><code>child</code> does not have a parent element
0479: * </ul></dd></dl>
0480: *
0481: * <dl><dt><b>Postconditions:</b></dt><dd>
0482: * <ul><li>countChildren() => old.countChildren() + 1
0483: * <li>enumerateChildren() => old.enumerateChildren() + child
0484: * <li>getChildren() => old.enumerateChildren() + child
0485: * </ul></dd></dl><dl>
0486: *
0487: * @see nanoxml.XMLElement#countChildren()
0488: * @see nanoxml.XMLElement#enumerateChildren()
0489: * @see nanoxml.XMLElement#getChildren()
0490: * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
0491: * removeChild(XMLElement)
0492: */
0493: public void addChild(XMLElement child) {
0494: this .children.addElement(child);
0495: }
0496:
0497: /**
0498: * Adds or modifies an attribute.
0499: *
0500: * @param name
0501: * The name of the attribute.
0502: * @param value
0503: * The value of the attribute.
0504: *
0505: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0506: * <ul><li><code>name != null</code>
0507: * <li><code>name</code> is a valid XML identifier
0508: * <li><code>value != null</code>
0509: * </ul></dd></dl>
0510: *
0511: * <dl><dt><b>Postconditions:</b></dt><dd>
0512: * <ul><li>enumerateAttributeNames()
0513: * => old.enumerateAttributeNames() + name
0514: * <li>getAttribute(name) => value
0515: * </ul></dd></dl><dl>
0516: *
0517: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
0518: * setDoubleAttribute(String, double)
0519: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
0520: * setIntAttribute(String, int)
0521: * @see nanoxml.XMLElement#enumerateAttributeNames()
0522: * @see nanoxml.XMLElement#getAttribute(java.lang.String)
0523: * getAttribute(String)
0524: * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
0525: * getAttribute(String, Object)
0526: * @see nanoxml.XMLElement#getAttribute(java.lang.String,
0527: * java.util.Hashtable,
0528: * java.lang.String, boolean)
0529: * getAttribute(String, Hashtable, String, boolean)
0530: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
0531: * getStringAttribute(String)
0532: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0533: * java.lang.String)
0534: * getStringAttribute(String, String)
0535: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0536: * java.util.Hashtable,
0537: * java.lang.String, boolean)
0538: * getStringAttribute(String, Hashtable, String, boolean)
0539: */
0540: public void setAttribute(String name, Object value) {
0541: if (this .ignoreCase) {
0542: name = name.toUpperCase();
0543: }
0544: this .attributes.put(name, value.toString());
0545: }
0546:
0547: /**
0548: * Adds or modifies an attribute.
0549: *
0550: * @param name
0551: * The name of the attribute.
0552: * @param value
0553: * The value of the attribute.
0554: *
0555: * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object)
0556: * setAttribute} instead.
0557: */
0558: public void addProperty(String name, Object value) {
0559: this .setAttribute(name, value);
0560: }
0561:
0562: /**
0563: * Adds or modifies an attribute.
0564: *
0565: * @param name
0566: * The name of the attribute.
0567: * @param value
0568: * The value of the attribute.
0569: *
0570: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0571: * <ul><li><code>name != null</code>
0572: * <li><code>name</code> is a valid XML identifier
0573: * </ul></dd></dl>
0574: *
0575: * <dl><dt><b>Postconditions:</b></dt><dd>
0576: * <ul><li>enumerateAttributeNames()
0577: * => old.enumerateAttributeNames() + name
0578: * <li>getIntAttribute(name) => value
0579: * </ul></dd></dl><dl>
0580: *
0581: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
0582: * setDoubleAttribute(String, double)
0583: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0584: * setAttribute(String, Object)
0585: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0586: * removeAttribute(String)
0587: * @see nanoxml.XMLElement#enumerateAttributeNames()
0588: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
0589: * getIntAttribute(String)
0590: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
0591: * getIntAttribute(String, int)
0592: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
0593: * java.util.Hashtable,
0594: * java.lang.String, boolean)
0595: * getIntAttribute(String, Hashtable, String, boolean)
0596: */
0597: public void setIntAttribute(String name, int value) {
0598: if (this .ignoreCase) {
0599: name = name.toUpperCase();
0600: }
0601: this .attributes.put(name, Integer.toString(value));
0602: }
0603:
0604: /**
0605: * Adds or modifies an attribute.
0606: *
0607: * @param name
0608: * The name of the attribute.
0609: * @param value
0610: * The value of the attribute.
0611: *
0612: * @deprecated Use {@link #setIntAttribute(java.lang.String, int)
0613: * setIntAttribute} instead.
0614: */
0615: public void addProperty(String key, int value) {
0616: this .setIntAttribute(key, value);
0617: }
0618:
0619: /**
0620: * Adds or modifies an attribute.
0621: *
0622: * @param name
0623: * The name of the attribute.
0624: * @param value
0625: * The value of the attribute.
0626: *
0627: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0628: * <ul><li><code>name != null</code>
0629: * <li><code>name</code> is a valid XML identifier
0630: * </ul></dd></dl>
0631: *
0632: * <dl><dt><b>Postconditions:</b></dt><dd>
0633: * <ul><li>enumerateAttributeNames()
0634: * => old.enumerateAttributeNames() + name
0635: * <li>getDoubleAttribute(name) => value
0636: * </ul></dd></dl><dl>
0637: *
0638: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
0639: * setIntAttribute(String, int)
0640: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0641: * setAttribute(String, Object)
0642: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0643: * removeAttribute(String)
0644: * @see nanoxml.XMLElement#enumerateAttributeNames()
0645: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
0646: * getDoubleAttribute(String)
0647: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
0648: * getDoubleAttribute(String, double)
0649: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
0650: * java.util.Hashtable,
0651: * java.lang.String, boolean)
0652: * getDoubleAttribute(String, Hashtable, String, boolean)
0653: */
0654: public void setDoubleAttribute(String name, double value) {
0655: if (this .ignoreCase) {
0656: name = name.toUpperCase();
0657: }
0658: this .attributes.put(name, Double.toString(value));
0659: }
0660:
0661: /**
0662: * Adds or modifies an attribute.
0663: *
0664: * @param name
0665: * The name of the attribute.
0666: * @param value
0667: * The value of the attribute.
0668: *
0669: * @deprecated Use {@link #setDoubleAttribute(java.lang.String, double)
0670: * setDoubleAttribute} instead.
0671: */
0672: public void addProperty(String name, double value) {
0673: this .setDoubleAttribute(name, value);
0674: }
0675:
0676: /**
0677: * Returns the number of child elements of the element.
0678: *
0679: * <dl><dt><b>Postconditions:</b></dt><dd>
0680: * <ul><li><code>result >= 0</code>
0681: * </ul></dd></dl>
0682: *
0683: * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
0684: * addChild(XMLElement)
0685: * @see nanoxml.XMLElement#enumerateChildren()
0686: * @see nanoxml.XMLElement#getChildren()
0687: * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
0688: * removeChild(XMLElement)
0689: */
0690: public int countChildren() {
0691: return this .children.size();
0692: }
0693:
0694: /**
0695: * Enumerates the attribute names.
0696: *
0697: * <dl><dt><b>Postconditions:</b></dt><dd>
0698: * <ul><li><code>result != null</code>
0699: * </ul></dd></dl>
0700: *
0701: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
0702: * setDoubleAttribute(String, double)
0703: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
0704: * setIntAttribute(String, int)
0705: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0706: * setAttribute(String, Object)
0707: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0708: * removeAttribute(String)
0709: * @see nanoxml.XMLElement#getAttribute(java.lang.String)
0710: * getAttribute(String)
0711: * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
0712: * getAttribute(String, String)
0713: * @see nanoxml.XMLElement#getAttribute(java.lang.String,
0714: * java.util.Hashtable,
0715: * java.lang.String, boolean)
0716: * getAttribute(String, Hashtable, String, boolean)
0717: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
0718: * getStringAttribute(String)
0719: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0720: * java.lang.String)
0721: * getStringAttribute(String, String)
0722: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0723: * java.util.Hashtable,
0724: * java.lang.String, boolean)
0725: * getStringAttribute(String, Hashtable, String, boolean)
0726: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
0727: * getIntAttribute(String)
0728: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
0729: * getIntAttribute(String, int)
0730: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
0731: * java.util.Hashtable,
0732: * java.lang.String, boolean)
0733: * getIntAttribute(String, Hashtable, String, boolean)
0734: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
0735: * getDoubleAttribute(String)
0736: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
0737: * getDoubleAttribute(String, double)
0738: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
0739: * java.util.Hashtable,
0740: * java.lang.String, boolean)
0741: * getDoubleAttribute(String, Hashtable, String, boolean)
0742: * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
0743: * java.lang.String,
0744: * java.lang.String, boolean)
0745: * getBooleanAttribute(String, String, String, boolean)
0746: */
0747: public Enumeration enumerateAttributeNames() {
0748: return this .attributes.keys();
0749: }
0750:
0751: /**
0752: * Enumerates the attribute names.
0753: *
0754: * @deprecated Use {@link #enumerateAttributeNames()
0755: * enumerateAttributeNames} instead.
0756: */
0757: public Enumeration enumeratePropertyNames() {
0758: return this .enumerateAttributeNames();
0759: }
0760:
0761: /**
0762: * Enumerates the child elements.
0763: *
0764: * <dl><dt><b>Postconditions:</b></dt><dd>
0765: * <ul><li><code>result != null</code>
0766: * </ul></dd></dl>
0767: *
0768: * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
0769: * addChild(XMLElement)
0770: * @see nanoxml.XMLElement#countChildren()
0771: * @see nanoxml.XMLElement#getChildren()
0772: * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
0773: * removeChild(XMLElement)
0774: */
0775: public Enumeration enumerateChildren() {
0776: return this .children.elements();
0777: }
0778:
0779: /**
0780: * Returns the child elements as a Vector. It is safe to modify this
0781: * Vector.
0782: *
0783: * <dl><dt><b>Postconditions:</b></dt><dd>
0784: * <ul><li><code>result != null</code>
0785: * </ul></dd></dl>
0786: *
0787: * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
0788: * addChild(XMLElement)
0789: * @see nanoxml.XMLElement#countChildren()
0790: * @see nanoxml.XMLElement#enumerateChildren()
0791: * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
0792: * removeChild(XMLElement)
0793: */
0794: public Vector getChildren() {
0795: try {
0796: return (Vector) this .children.clone();
0797: } catch (Exception e) {
0798: // this never happens, however, some Java compilers are so
0799: // braindead that they require this exception clause
0800: return null;
0801: }
0802: }
0803:
0804: /**
0805: * Returns the PCDATA content of the object. If there is no such content,
0806: * <CODE>null</CODE> is returned.
0807: *
0808: * @deprecated Use {@link #getContent() getContent} instead.
0809: */
0810: public String getContents() {
0811: return this .getContent();
0812: }
0813:
0814: /**
0815: * Returns the PCDATA content of the object. If there is no such content,
0816: * <CODE>null</CODE> is returned.
0817: *
0818: * @see nanoxml.XMLElement#setContent(java.lang.String)
0819: * setContent(String)
0820: */
0821: public String getContent() {
0822: return this .contents;
0823: }
0824:
0825: /**
0826: * Returns the line nr in the source data on which the element is found.
0827: * This method returns <code>0</code> there is no associated source data.
0828: *
0829: * <dl><dt><b>Postconditions:</b></dt><dd>
0830: * <ul><li><code>result >= 0</code>
0831: * </ul></dd></dl>
0832: */
0833: public int getLineNr() {
0834: return this .lineNr;
0835: }
0836:
0837: /**
0838: * Returns an attribute of the element.
0839: * If the attribute doesn't exist, <code>null</code> is returned.
0840: *
0841: * @param name The name of the attribute.
0842: *
0843: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0844: * <ul><li><code>name != null</code>
0845: * <li><code>name</code> is a valid XML identifier
0846: * </ul></dd></dl><dl>
0847: *
0848: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0849: * setAttribute(String, Object)
0850: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0851: * removeAttribute(String)
0852: * @see nanoxml.XMLElement#enumerateAttributeNames()
0853: * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
0854: * getAttribute(String, Object)
0855: * @see nanoxml.XMLElement#getAttribute(java.lang.String,
0856: * java.util.Hashtable,
0857: * java.lang.String, boolean)
0858: * getAttribute(String, Hashtable, String, boolean)
0859: */
0860: public Object getAttribute(String name) {
0861: return this .getAttribute(name, null);
0862: }
0863:
0864: /**
0865: * Returns an attribute of the element.
0866: * If the attribute doesn't exist, <code>defaultValue</code> is returned.
0867: *
0868: * @param name The name of the attribute.
0869: * @param defaultValue Key to use if the attribute is missing.
0870: *
0871: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0872: * <ul><li><code>name != null</code>
0873: * <li><code>name</code> is a valid XML identifier
0874: * </ul></dd></dl><dl>
0875: *
0876: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0877: * setAttribute(String, Object)
0878: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0879: * removeAttribute(String)
0880: * @see nanoxml.XMLElement#enumerateAttributeNames()
0881: * @see nanoxml.XMLElement#getAttribute(java.lang.String)
0882: * getAttribute(String)
0883: * @see nanoxml.XMLElement#getAttribute(java.lang.String,
0884: * java.util.Hashtable,
0885: * java.lang.String, boolean)
0886: * getAttribute(String, Hashtable, String, boolean)
0887: */
0888: public Object getAttribute(String name, Object defaultValue) {
0889: if (this .ignoreCase) {
0890: name = name.toUpperCase();
0891: }
0892: Object value = this .attributes.get(name);
0893: if (value == null) {
0894: value = defaultValue;
0895: }
0896: return value;
0897: }
0898:
0899: /**
0900: * Returns an attribute by looking up a key in a hashtable.
0901: * If the attribute doesn't exist, the value corresponding to defaultKey
0902: * is returned.
0903: * <P>
0904: * As an example, if valueSet contains the mapping <code>"one" =>
0905: * "1"</code>
0906: * and the element contains the attribute <code>attr="one"</code>, then
0907: * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
0908: * <code>"1"</code>.
0909: *
0910: * @param name
0911: * The name of the attribute.
0912: * @param valueSet
0913: * Hashtable mapping keys to values.
0914: * @param defaultKey
0915: * Key to use if the attribute is missing.
0916: * @param allowLiterals
0917: * <code>true</code> if literals are valid.
0918: *
0919: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0920: * <ul><li><code>name != null</code>
0921: * <li><code>name</code> is a valid XML identifier
0922: * <li><code>valueSet</code> != null
0923: * <li>the keys of <code>valueSet</code> are strings
0924: * </ul></dd></dl><dl>
0925: *
0926: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0927: * setAttribute(String, Object)
0928: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0929: * removeAttribute(String)
0930: * @see nanoxml.XMLElement#enumerateAttributeNames()
0931: * @see nanoxml.XMLElement#getAttribute(java.lang.String)
0932: * getAttribute(String)
0933: * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
0934: * getAttribute(String, Object)
0935: */
0936: public Object getAttribute(String name, Hashtable valueSet,
0937: String defaultKey, boolean allowLiterals) {
0938: if (this .ignoreCase) {
0939: name = name.toUpperCase();
0940: }
0941: Object key = this .attributes.get(name);
0942: Object result;
0943: if (key == null) {
0944: key = defaultKey;
0945: }
0946: result = valueSet.get(key);
0947: if (result == null) {
0948: if (allowLiterals) {
0949: result = key;
0950: } else {
0951: throw this .invalidValue(name, (String) key);
0952: }
0953: }
0954: return result;
0955: }
0956:
0957: /**
0958: * Returns an attribute of the element.
0959: * If the attribute doesn't exist, <code>null</code> is returned.
0960: *
0961: * @param name The name of the attribute.
0962: *
0963: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0964: * <ul><li><code>name != null</code>
0965: * <li><code>name</code> is a valid XML identifier
0966: * </ul></dd></dl><dl>
0967: *
0968: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0969: * setAttribute(String, Object)
0970: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
0971: * removeAttribute(String)
0972: * @see nanoxml.XMLElement#enumerateAttributeNames()
0973: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0974: * java.lang.String)
0975: * getStringAttribute(String, String)
0976: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
0977: * java.util.Hashtable,
0978: * java.lang.String, boolean)
0979: * getStringAttribute(String, Hashtable, String, boolean)
0980: */
0981: public String getStringAttribute(String name) {
0982: return this .getStringAttribute(name, null);
0983: }
0984:
0985: /**
0986: * Returns an attribute of the element.
0987: * If the attribute doesn't exist, <code>defaultValue</code> is returned.
0988: *
0989: * @param name The name of the attribute.
0990: * @param defaultValue Key to use if the attribute is missing.
0991: *
0992: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
0993: * <ul><li><code>name != null</code>
0994: * <li><code>name</code> is a valid XML identifier
0995: * </ul></dd></dl><dl>
0996: *
0997: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
0998: * setAttribute(String, Object)
0999: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1000: * removeAttribute(String)
1001: * @see nanoxml.XMLElement#enumerateAttributeNames()
1002: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
1003: * getStringAttribute(String)
1004: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
1005: * java.util.Hashtable,
1006: * java.lang.String, boolean)
1007: * getStringAttribute(String, Hashtable, String, boolean)
1008: */
1009: public String getStringAttribute(String name, String defaultValue) {
1010: return (String) this .getAttribute(name, defaultValue);
1011: }
1012:
1013: /**
1014: * Returns an attribute by looking up a key in a hashtable.
1015: * If the attribute doesn't exist, the value corresponding to defaultKey
1016: * is returned.
1017: * <P>
1018: * As an example, if valueSet contains the mapping <code>"one" =>
1019: * "1"</code>
1020: * and the element contains the attribute <code>attr="one"</code>, then
1021: * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
1022: * <code>"1"</code>.
1023: *
1024: * @param name
1025: * The name of the attribute.
1026: * @param valueSet
1027: * Hashtable mapping keys to values.
1028: * @param defaultKey
1029: * Key to use if the attribute is missing.
1030: * @param allowLiterals
1031: * <code>true</code> if literals are valid.
1032: *
1033: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1034: * <ul><li><code>name != null</code>
1035: * <li><code>name</code> is a valid XML identifier
1036: * <li><code>valueSet</code> != null
1037: * <li>the keys of <code>valueSet</code> are strings
1038: * <li>the values of <code>valueSet</code> are strings
1039: * </ul></dd></dl><dl>
1040: *
1041: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1042: * setAttribute(String, Object)
1043: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1044: * removeAttribute(String)
1045: * @see nanoxml.XMLElement#enumerateAttributeNames()
1046: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
1047: * getStringAttribute(String)
1048: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
1049: * java.lang.String)
1050: * getStringAttribute(String, String)
1051: */
1052: public String getStringAttribute(String name, Hashtable valueSet,
1053: String defaultKey, boolean allowLiterals) {
1054: return (String) this .getAttribute(name, valueSet, defaultKey,
1055: allowLiterals);
1056: }
1057:
1058: /**
1059: * Returns an attribute of the element.
1060: * If the attribute doesn't exist, <code>0</code> is returned.
1061: *
1062: * @param name The name of the attribute.
1063: *
1064: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1065: * <ul><li><code>name != null</code>
1066: * <li><code>name</code> is a valid XML identifier
1067: * </ul></dd></dl><dl>
1068: *
1069: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
1070: * setIntAttribute(String, int)
1071: * @see nanoxml.XMLElement#enumerateAttributeNames()
1072: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
1073: * getIntAttribute(String, int)
1074: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
1075: * java.util.Hashtable,
1076: * java.lang.String, boolean)
1077: * getIntAttribute(String, Hashtable, String, boolean)
1078: */
1079: public int getIntAttribute(String name) {
1080: return this .getIntAttribute(name, 0);
1081: }
1082:
1083: /**
1084: * Returns an attribute of the element.
1085: * If the attribute doesn't exist, <code>defaultValue</code> is returned.
1086: *
1087: * @param name The name of the attribute.
1088: * @param defaultValue Key to use if the attribute is missing.
1089: *
1090: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1091: * <ul><li><code>name != null</code>
1092: * <li><code>name</code> is a valid XML identifier
1093: * </ul></dd></dl><dl>
1094: *
1095: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
1096: * setIntAttribute(String, int)
1097: * @see nanoxml.XMLElement#enumerateAttributeNames()
1098: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
1099: * getIntAttribute(String)
1100: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
1101: * java.util.Hashtable,
1102: * java.lang.String, boolean)
1103: * getIntAttribute(String, Hashtable, String, boolean)
1104: */
1105: public int getIntAttribute(String name, int defaultValue) {
1106: if (this .ignoreCase) {
1107: name = name.toUpperCase();
1108: }
1109: String value = (String) this .attributes.get(name);
1110: if (value == null) {
1111: return defaultValue;
1112: } else {
1113: try {
1114: return Integer.parseInt(value);
1115: } catch (NumberFormatException e) {
1116: throw this .invalidValue(name, value);
1117: }
1118: }
1119: }
1120:
1121: /**
1122: * Returns an attribute by looking up a key in a hashtable.
1123: * If the attribute doesn't exist, the value corresponding to defaultKey
1124: * is returned.
1125: * <P>
1126: * As an example, if valueSet contains the mapping <code>"one" => 1</code>
1127: * and the element contains the attribute <code>attr="one"</code>, then
1128: * <code>getIntAttribute("attr", mapping, defaultKey, false)</code> returns
1129: * <code>1</code>.
1130: *
1131: * @param name
1132: * The name of the attribute.
1133: * @param valueSet
1134: * Hashtable mapping keys to values.
1135: * @param defaultKey
1136: * Key to use if the attribute is missing.
1137: * @param allowLiteralNumbers
1138: * <code>true</code> if literal numbers are valid.
1139: *
1140: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1141: * <ul><li><code>name != null</code>
1142: * <li><code>name</code> is a valid XML identifier
1143: * <li><code>valueSet</code> != null
1144: * <li>the keys of <code>valueSet</code> are strings
1145: * <li>the values of <code>valueSet</code> are Integer objects
1146: * <li><code>defaultKey</code> is either <code>null</code>, a
1147: * key in <code>valueSet</code> or an integer.
1148: * </ul></dd></dl><dl>
1149: *
1150: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
1151: * setIntAttribute(String, int)
1152: * @see nanoxml.XMLElement#enumerateAttributeNames()
1153: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
1154: * getIntAttribute(String)
1155: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
1156: * getIntAttribute(String, int)
1157: */
1158: public int getIntAttribute(String name, Hashtable valueSet,
1159: String defaultKey, boolean allowLiteralNumbers) {
1160: if (this .ignoreCase) {
1161: name = name.toUpperCase();
1162: }
1163: Object key = this .attributes.get(name);
1164: Integer result;
1165: if (key == null) {
1166: key = defaultKey;
1167: }
1168: try {
1169: result = (Integer) valueSet.get(key);
1170: } catch (ClassCastException e) {
1171: throw this .invalidValueSet(name);
1172: }
1173: if (result == null) {
1174: if (!allowLiteralNumbers) {
1175: throw this .invalidValue(name, (String) key);
1176: }
1177: try {
1178: result = Integer.valueOf((String) key);
1179: } catch (NumberFormatException e) {
1180: throw this .invalidValue(name, (String) key);
1181: }
1182: }
1183: return result.intValue();
1184: }
1185:
1186: /**
1187: * Returns an attribute of the element.
1188: * If the attribute doesn't exist, <code>0.0</code> is returned.
1189: *
1190: * @param name The name of the attribute.
1191: *
1192: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1193: * <ul><li><code>name != null</code>
1194: * <li><code>name</code> is a valid XML identifier
1195: * </ul></dd></dl><dl>
1196: *
1197: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
1198: * setDoubleAttribute(String, double)
1199: * @see nanoxml.XMLElement#enumerateAttributeNames()
1200: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
1201: * getDoubleAttribute(String, double)
1202: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
1203: * java.util.Hashtable,
1204: * java.lang.String, boolean)
1205: * getDoubleAttribute(String, Hashtable, String, boolean)
1206: */
1207: public double getDoubleAttribute(String name) {
1208: return this .getDoubleAttribute(name, 0.);
1209: }
1210:
1211: /**
1212: * Returns an attribute of the element.
1213: * If the attribute doesn't exist, <code>defaultValue</code> is returned.
1214: *
1215: * @param name The name of the attribute.
1216: * @param defaultValue Key to use if the attribute is missing.
1217: *
1218: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1219: * <ul><li><code>name != null</code>
1220: * <li><code>name</code> is a valid XML identifier
1221: * </ul></dd></dl><dl>
1222: *
1223: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
1224: * setDoubleAttribute(String, double)
1225: * @see nanoxml.XMLElement#enumerateAttributeNames()
1226: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
1227: * getDoubleAttribute(String)
1228: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
1229: * java.util.Hashtable,
1230: * java.lang.String, boolean)
1231: * getDoubleAttribute(String, Hashtable, String, boolean)
1232: */
1233: public double getDoubleAttribute(String name, double defaultValue) {
1234: if (this .ignoreCase) {
1235: name = name.toUpperCase();
1236: }
1237: String value = (String) this .attributes.get(name);
1238: if (value == null) {
1239: return defaultValue;
1240: } else {
1241: try {
1242: return Double.valueOf(value).doubleValue();
1243: } catch (NumberFormatException e) {
1244: throw this .invalidValue(name, value);
1245: }
1246: }
1247: }
1248:
1249: /**
1250: * Returns an attribute by looking up a key in a hashtable.
1251: * If the attribute doesn't exist, the value corresponding to defaultKey
1252: * is returned.
1253: * <P>
1254: * As an example, if valueSet contains the mapping <code>"one" =>
1255: * 1.0</code>
1256: * and the element contains the attribute <code>attr="one"</code>, then
1257: * <code>getDoubleAttribute("attr", mapping, defaultKey, false)</code>
1258: * returns <code>1.0</code>.
1259: *
1260: * @param name
1261: * The name of the attribute.
1262: * @param valueSet
1263: * Hashtable mapping keys to values.
1264: * @param defaultKey
1265: * Key to use if the attribute is missing.
1266: * @param allowLiteralNumbers
1267: * <code>true</code> if literal numbers are valid.
1268: *
1269: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1270: * <ul><li><code>name != null</code>
1271: * <li><code>name</code> is a valid XML identifier
1272: * <li><code>valueSet != null</code>
1273: * <li>the keys of <code>valueSet</code> are strings
1274: * <li>the values of <code>valueSet</code> are Double objects
1275: * <li><code>defaultKey</code> is either <code>null</code>, a
1276: * key in <code>valueSet</code> or a double.
1277: * </ul></dd></dl><dl>
1278: *
1279: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
1280: * setDoubleAttribute(String, double)
1281: * @see nanoxml.XMLElement#enumerateAttributeNames()
1282: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
1283: * getDoubleAttribute(String)
1284: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
1285: * getDoubleAttribute(String, double)
1286: */
1287: public double getDoubleAttribute(String name, Hashtable valueSet,
1288: String defaultKey, boolean allowLiteralNumbers) {
1289: if (this .ignoreCase) {
1290: name = name.toUpperCase();
1291: }
1292: Object key = this .attributes.get(name);
1293: Double result;
1294: if (key == null) {
1295: key = defaultKey;
1296: }
1297: try {
1298: result = (Double) valueSet.get(key);
1299: } catch (ClassCastException e) {
1300: throw this .invalidValueSet(name);
1301: }
1302: if (result == null) {
1303: if (!allowLiteralNumbers) {
1304: throw this .invalidValue(name, (String) key);
1305: }
1306: try {
1307: result = Double.valueOf((String) key);
1308: } catch (NumberFormatException e) {
1309: throw this .invalidValue(name, (String) key);
1310: }
1311: }
1312: return result.doubleValue();
1313: }
1314:
1315: /**
1316: * Returns an attribute of the element.
1317: * If the attribute doesn't exist, <code>defaultValue</code> is returned.
1318: * If the value of the attribute is equal to <code>trueValue</code>,
1319: * <code>true</code> is returned.
1320: * If the value of the attribute is equal to <code>falseValue</code>,
1321: * <code>false</code> is returned.
1322: * If the value doesn't match <code>trueValue</code> or
1323: * <code>falseValue</code>, an exception is thrown.
1324: *
1325: * @param name The name of the attribute.
1326: * @param trueValue The value associated with <code>true</code>.
1327: * @param falseValue The value associated with <code>true</code>.
1328: * @param defaultValue Value to use if the attribute is missing.
1329: *
1330: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1331: * <ul><li><code>name != null</code>
1332: * <li><code>name</code> is a valid XML identifier
1333: * <li><code>trueValue</code> and <code>falseValue</code>
1334: * are different strings.
1335: * </ul></dd></dl><dl>
1336: *
1337: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1338: * setAttribute(String, Object)
1339: * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1340: * removeAttribute(String)
1341: * @see nanoxml.XMLElement#enumerateAttributeNames()
1342: */
1343: public boolean getBooleanAttribute(String name, String trueValue,
1344: String falseValue, boolean defaultValue) {
1345: if (this .ignoreCase) {
1346: name = name.toUpperCase();
1347: }
1348: Object value = this .attributes.get(name);
1349: if (value == null) {
1350: return defaultValue;
1351: } else if (value.equals(trueValue)) {
1352: return true;
1353: } else if (value.equals(falseValue)) {
1354: return false;
1355: } else {
1356: throw this .invalidValue(name, (String) value);
1357: }
1358: }
1359:
1360: /**
1361: * Returns an attribute by looking up a key in a hashtable.
1362: *
1363: * @deprecated Use {@link #getIntAttribute(java.lang.String,
1364: * java.util.Hashtable, java.lang.String, boolean)
1365: * getIntAttribute} instead.
1366: */
1367: public int getIntProperty(String name, Hashtable valueSet,
1368: String defaultKey) {
1369: return this .getIntAttribute(name, valueSet, defaultKey, false);
1370: }
1371:
1372: /**
1373: * Returns an attribute.
1374: *
1375: * @deprecated Use {@link #getStringAttribute(java.lang.String)
1376: * getStringAttribute} instead.
1377: */
1378: public String getProperty(String name) {
1379: return this .getStringAttribute(name);
1380: }
1381:
1382: /**
1383: * Returns an attribute.
1384: *
1385: * @deprecated Use {@link #getStringAttribute(java.lang.String,
1386: * java.lang.String) getStringAttribute} instead.
1387: */
1388: public String getProperty(String name, String defaultValue) {
1389: return this .getStringAttribute(name, defaultValue);
1390: }
1391:
1392: /**
1393: * Returns an attribute.
1394: *
1395: * @deprecated Use {@link #getIntAttribute(java.lang.String, int)
1396: * getIntAttribute} instead.
1397: */
1398: public int getProperty(String name, int defaultValue) {
1399: return this .getIntAttribute(name, defaultValue);
1400: }
1401:
1402: /**
1403: * Returns an attribute.
1404: *
1405: * @deprecated Use {@link #getDoubleAttribute(java.lang.String, double)
1406: * getDoubleAttribute} instead.
1407: */
1408: public double getProperty(String name, double defaultValue) {
1409: return this .getDoubleAttribute(name, defaultValue);
1410: }
1411:
1412: /**
1413: * Returns an attribute.
1414: *
1415: * @deprecated Use {@link #getBooleanAttribute(java.lang.String,
1416: * java.lang.String, java.lang.String, boolean)
1417: * getBooleanAttribute} instead.
1418: */
1419: public boolean getProperty(String key, String trueValue,
1420: String falseValue, boolean defaultValue) {
1421: return this .getBooleanAttribute(key, trueValue, falseValue,
1422: defaultValue);
1423: }
1424:
1425: /**
1426: * Returns an attribute by looking up a key in a hashtable.
1427: *
1428: * @deprecated Use {@link #getAttribute(java.lang.String,
1429: * java.util.Hashtable, java.lang.String, boolean)
1430: * getAttribute} instead.
1431: */
1432: public Object getProperty(String name, Hashtable valueSet,
1433: String defaultKey) {
1434: return this .getAttribute(name, valueSet, defaultKey, false);
1435: }
1436:
1437: /**
1438: * Returns an attribute by looking up a key in a hashtable.
1439: *
1440: * @deprecated Use {@link #getStringAttribute(java.lang.String,
1441: * java.util.Hashtable, java.lang.String, boolean)
1442: * getStringAttribute} instead.
1443: */
1444: public String getStringProperty(String name, Hashtable valueSet,
1445: String defaultKey) {
1446: return this .getStringAttribute(name, valueSet, defaultKey,
1447: false);
1448: }
1449:
1450: /**
1451: * Returns an attribute by looking up a key in a hashtable.
1452: *
1453: * @deprecated Use {@link #getIntAttribute(java.lang.String,
1454: * java.util.Hashtable, java.lang.String, boolean)
1455: * getIntAttribute} instead.
1456: */
1457: public int getSpecialIntProperty(String name, Hashtable valueSet,
1458: String defaultKey) {
1459: return this .getIntAttribute(name, valueSet, defaultKey, true);
1460: }
1461:
1462: /**
1463: * Returns an attribute by looking up a key in a hashtable.
1464: *
1465: * @deprecated Use {@link #getDoubleAttribute(java.lang.String,
1466: * java.util.Hashtable, java.lang.String, boolean)
1467: * getDoubleAttribute} instead.
1468: */
1469: public double getSpecialDoubleProperty(String name,
1470: Hashtable valueSet, String defaultKey) {
1471: return this
1472: .getDoubleAttribute(name, valueSet, defaultKey, true);
1473: }
1474:
1475: /**
1476: * Returns the name of the element.
1477: *
1478: * @see nanoxml.XMLElement#setName(java.lang.String) setName(String)
1479: */
1480: public String getName() {
1481: return this .name;
1482: }
1483:
1484: /**
1485: * Returns the name of the element.
1486: *
1487: * @deprecated Use {@link #getName() getName} instead.
1488: */
1489: public String getTagName() {
1490: return this .getName();
1491: }
1492:
1493: /**
1494: * Reads one XML element from a java.io.Reader and parses it.
1495: *
1496: * @param reader
1497: * The reader from which to retrieve the XML data.
1498: *
1499: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1500: * <ul><li><code>reader != null</code>
1501: * <li><code>reader</code> is not closed
1502: * </ul></dd></dl>
1503: *
1504: * <dl><dt><b>Postconditions:</b></dt><dd>
1505: * <ul><li>the state of the receiver is updated to reflect the XML element
1506: * parsed from the reader
1507: * <li>the reader points to the first character following the last
1508: * '>' character of the XML element
1509: * </ul></dd></dl><dl>
1510: *
1511: * @throws java.io.IOException
1512: * If an error occured while reading the input.
1513: * @throws nanoxml.XMLParseException
1514: * If an error occured while parsing the read data.
1515: */
1516: public void parseFromReader(Reader reader) throws IOException,
1517: XMLParseException {
1518: this .parseFromReader(reader, /*startingLineNr*/1);
1519: }
1520:
1521: /**
1522: * Reads one XML element from a java.io.Reader and parses it.
1523: *
1524: * @param reader
1525: * The reader from which to retrieve the XML data.
1526: * @param startingLineNr
1527: * The line number of the first line in the data.
1528: *
1529: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1530: * <ul><li><code>reader != null</code>
1531: * <li><code>reader</code> is not closed
1532: * </ul></dd></dl>
1533: *
1534: * <dl><dt><b>Postconditions:</b></dt><dd>
1535: * <ul><li>the state of the receiver is updated to reflect the XML element
1536: * parsed from the reader
1537: * <li>the reader points to the first character following the last
1538: * '>' character of the XML element
1539: * </ul></dd></dl><dl>
1540: *
1541: * @throws java.io.IOException
1542: * If an error occured while reading the input.
1543: * @throws nanoxml.XMLParseException
1544: * If an error occured while parsing the read data.
1545: */
1546: public void parseFromReader(Reader reader, int startingLineNr)
1547: throws IOException, XMLParseException {
1548: this .name = null;
1549: this .contents = "";
1550: this .attributes = new Hashtable();
1551: this .children = new Vector();
1552: this .charReadTooMuch = '\0';
1553: this .reader = reader;
1554: this .parserLineNr = startingLineNr;
1555:
1556: for (;;) {
1557: char ch = this .scanWhitespace();
1558:
1559: if (ch != '<') {
1560: throw this .expectedInput("<");
1561: }
1562:
1563: ch = this .readChar();
1564:
1565: if ((ch == '!') || (ch == '?')) {
1566: this .skipSpecialTag(0);
1567: } else {
1568: this .unreadChar(ch);
1569: this .scanElement(this );
1570: return;
1571: }
1572: }
1573: }
1574:
1575: /**
1576: * Reads one XML element from a String and parses it.
1577: *
1578: * @param reader
1579: * The reader from which to retrieve the XML data.
1580: *
1581: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1582: * <ul><li><code>string != null</code>
1583: * <li><code>string.length() > 0</code>
1584: * </ul></dd></dl>
1585: *
1586: * <dl><dt><b>Postconditions:</b></dt><dd>
1587: * <ul><li>the state of the receiver is updated to reflect the XML element
1588: * parsed from the reader
1589: * </ul></dd></dl><dl>
1590: *
1591: * @throws nanoxml.XMLParseException
1592: * If an error occured while parsing the string.
1593: */
1594: public void parseString(String string) throws XMLParseException {
1595: try {
1596: this .parseFromReader(new StringReader(string),
1597: /*startingLineNr*/1);
1598: } catch (IOException e) {
1599: // Java exception handling suxx
1600: }
1601: }
1602:
1603: /**
1604: * Reads one XML element from a String and parses it.
1605: *
1606: * @param reader
1607: * The reader from which to retrieve the XML data.
1608: * @param offset
1609: * The first character in <code>string</code> to scan.
1610: *
1611: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1612: * <ul><li><code>string != null</code>
1613: * <li><code>offset < string.length()</code>
1614: * <li><code>offset >= 0</code>
1615: * </ul></dd></dl>
1616: *
1617: * <dl><dt><b>Postconditions:</b></dt><dd>
1618: * <ul><li>the state of the receiver is updated to reflect the XML element
1619: * parsed from the reader
1620: * </ul></dd></dl><dl>
1621: *
1622: * @throws nanoxml.XMLParseException
1623: * If an error occured while parsing the string.
1624: */
1625: public void parseString(String string, int offset)
1626: throws XMLParseException {
1627: this .parseString(string.substring(offset));
1628: }
1629:
1630: /**
1631: * Reads one XML element from a String and parses it.
1632: *
1633: * @param reader
1634: * The reader from which to retrieve the XML data.
1635: * @param offset
1636: * The first character in <code>string</code> to scan.
1637: * @param end
1638: * The character where to stop scanning.
1639: * This character is not scanned.
1640: *
1641: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1642: * <ul><li><code>string != null</code>
1643: * <li><code>end <= string.length()</code>
1644: * <li><code>offset < end</code>
1645: * <li><code>offset >= 0</code>
1646: * </ul></dd></dl>
1647: *
1648: * <dl><dt><b>Postconditions:</b></dt><dd>
1649: * <ul><li>the state of the receiver is updated to reflect the XML element
1650: * parsed from the reader
1651: * </ul></dd></dl><dl>
1652: *
1653: * @throws nanoxml.XMLParseException
1654: * If an error occured while parsing the string.
1655: */
1656: public void parseString(String string, int offset, int end)
1657: throws XMLParseException {
1658: this .parseString(string.substring(offset, end));
1659: }
1660:
1661: /**
1662: * Reads one XML element from a String and parses it.
1663: *
1664: * @param reader
1665: * The reader from which to retrieve the XML data.
1666: * @param offset
1667: * The first character in <code>string</code> to scan.
1668: * @param end
1669: * The character where to stop scanning.
1670: * This character is not scanned.
1671: * @param startingLineNr
1672: * The line number of the first line in the data.
1673: *
1674: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1675: * <ul><li><code>string != null</code>
1676: * <li><code>end <= string.length()</code>
1677: * <li><code>offset < end</code>
1678: * <li><code>offset >= 0</code>
1679: * </ul></dd></dl>
1680: *
1681: * <dl><dt><b>Postconditions:</b></dt><dd>
1682: * <ul><li>the state of the receiver is updated to reflect the XML element
1683: * parsed from the reader
1684: * </ul></dd></dl><dl>
1685: *
1686: * @throws nanoxml.XMLParseException
1687: * If an error occured while parsing the string.
1688: */
1689: public void parseString(String string, int offset, int end,
1690: int startingLineNr) throws XMLParseException {
1691: string = string.substring(offset, end);
1692: try {
1693: this .parseFromReader(new StringReader(string),
1694: startingLineNr);
1695: } catch (IOException e) {
1696: // Java exception handling suxx
1697: }
1698: }
1699:
1700: /**
1701: * Reads one XML element from a char array and parses it.
1702: *
1703: * @param reader
1704: * The reader from which to retrieve the XML data.
1705: * @param offset
1706: * The first character in <code>string</code> to scan.
1707: * @param end
1708: * The character where to stop scanning.
1709: * This character is not scanned.
1710: *
1711: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1712: * <ul><li><code>input != null</code>
1713: * <li><code>end <= input.length</code>
1714: * <li><code>offset < end</code>
1715: * <li><code>offset >= 0</code>
1716: * </ul></dd></dl>
1717: *
1718: * <dl><dt><b>Postconditions:</b></dt><dd>
1719: * <ul><li>the state of the receiver is updated to reflect the XML element
1720: * parsed from the reader
1721: * </ul></dd></dl><dl>
1722: *
1723: * @throws nanoxml.XMLParseException
1724: * If an error occured while parsing the string.
1725: */
1726: public void parseCharArray(char[] input, int offset, int end)
1727: throws XMLParseException {
1728: this .parseCharArray(input, offset, end, /*startingLineNr*/1);
1729: }
1730:
1731: /**
1732: * Reads one XML element from a char array and parses it.
1733: *
1734: * @param reader
1735: * The reader from which to retrieve the XML data.
1736: * @param offset
1737: * The first character in <code>string</code> to scan.
1738: * @param end
1739: * The character where to stop scanning.
1740: * This character is not scanned.
1741: * @param startingLineNr
1742: * The line number of the first line in the data.
1743: *
1744: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1745: * <ul><li><code>input != null</code>
1746: * <li><code>end <= input.length</code>
1747: * <li><code>offset < end</code>
1748: * <li><code>offset >= 0</code>
1749: * </ul></dd></dl>
1750: *
1751: * <dl><dt><b>Postconditions:</b></dt><dd>
1752: * <ul><li>the state of the receiver is updated to reflect the XML element
1753: * parsed from the reader
1754: * </ul></dd></dl><dl>
1755: *
1756: * @throws nanoxml.XMLParseException
1757: * If an error occured while parsing the string.
1758: */
1759: public void parseCharArray(char[] input, int offset, int end,
1760: int startingLineNr) throws XMLParseException {
1761: try {
1762: Reader reader = new CharArrayReader(input, offset, end);
1763: this .parseFromReader(reader, startingLineNr);
1764: } catch (IOException e) {
1765: // This exception will never happen.
1766: }
1767: }
1768:
1769: /**
1770: * Removes a child element.
1771: *
1772: * @param child
1773: * The child element to remove.
1774: *
1775: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1776: * <ul><li><code>child != null</code>
1777: * <li><code>child</code> is a child element of the receiver
1778: * </ul></dd></dl>
1779: *
1780: * <dl><dt><b>Postconditions:</b></dt><dd>
1781: * <ul><li>countChildren() => old.countChildren() - 1
1782: * <li>enumerateChildren() => old.enumerateChildren() - child
1783: * <li>getChildren() => old.enumerateChildren() - child
1784: * </ul></dd></dl><dl>
1785: *
1786: * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
1787: * addChild(XMLElement)
1788: * @see nanoxml.XMLElement#countChildren()
1789: * @see nanoxml.XMLElement#enumerateChildren()
1790: * @see nanoxml.XMLElement#getChildren()
1791: */
1792: public void removeChild(XMLElement child) {
1793: this .children.removeElement(child);
1794: }
1795:
1796: /**
1797: * Removes an attribute.
1798: *
1799: * @param name
1800: * The name of the attribute.
1801: *
1802: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1803: * <ul><li><code>name != null</code>
1804: * <li><code>name</code> is a valid XML identifier
1805: * </ul></dd></dl>
1806: *
1807: * <dl><dt><b>Postconditions:</b></dt><dd>
1808: * <ul><li>enumerateAttributeNames()
1809: * => old.enumerateAttributeNames() - name
1810: * <li>getAttribute(name) => <code>null</code>
1811: * </ul></dd></dl><dl>
1812: *
1813: * @see nanoxml.XMLElement#enumerateAttributeNames()
1814: * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
1815: * setDoubleAttribute(String, double)
1816: * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
1817: * setIntAttribute(String, int)
1818: * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1819: * setAttribute(String, Object)
1820: * @see nanoxml.XMLElement#getAttribute(java.lang.String)
1821: * getAttribute(String)
1822: * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
1823: * getAttribute(String, Object)
1824: * @see nanoxml.XMLElement#getAttribute(java.lang.String,
1825: * java.util.Hashtable,
1826: * java.lang.String, boolean)
1827: * getAttribute(String, Hashtable, String, boolean)
1828: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
1829: * getStringAttribute(String)
1830: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
1831: * java.lang.String)
1832: * getStringAttribute(String, String)
1833: * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
1834: * java.util.Hashtable,
1835: * java.lang.String, boolean)
1836: * getStringAttribute(String, Hashtable, String, boolean)
1837: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
1838: * getIntAttribute(String)
1839: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
1840: * getIntAttribute(String, int)
1841: * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
1842: * java.util.Hashtable,
1843: * java.lang.String, boolean)
1844: * getIntAttribute(String, Hashtable, String, boolean)
1845: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
1846: * getDoubleAttribute(String)
1847: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
1848: * getDoubleAttribute(String, double)
1849: * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
1850: * java.util.Hashtable,
1851: * java.lang.String, boolean)
1852: * getDoubleAttribute(String, Hashtable, String, boolean)
1853: * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
1854: * java.lang.String,
1855: * java.lang.String, boolean)
1856: * getBooleanAttribute(String, String, String, boolean)
1857: */
1858: public void removeAttribute(String name) {
1859: if (this .ignoreCase) {
1860: name = name.toUpperCase();
1861: }
1862: this .attributes.remove(name);
1863: }
1864:
1865: /**
1866: * Removes an attribute.
1867: *
1868: * @param name
1869: * The name of the attribute.
1870: *
1871: * @deprecated Use {@link #removeAttribute(java.lang.String)
1872: * removeAttribute} instead.
1873: */
1874: public void removeProperty(String name) {
1875: this .removeAttribute(name);
1876: }
1877:
1878: /**
1879: * Removes an attribute.
1880: *
1881: * @param name
1882: * The name of the attribute.
1883: *
1884: * @deprecated Use {@link #removeAttribute(java.lang.String)
1885: * removeAttribute} instead.
1886: */
1887: public void removeChild(String name) {
1888: this .removeAttribute(name);
1889: }
1890:
1891: /**
1892: * Creates a new similar XML element.
1893: * <P>
1894: * You should override this method when subclassing XMLElement.
1895: */
1896: protected XMLElement createAnotherElement() {
1897: return new XMLElement(this .entities, this .ignoreWhitespace,
1898: false, this .ignoreCase);
1899: }
1900:
1901: /**
1902: * Changes the content string.
1903: *
1904: * @param content
1905: * The new content string.
1906: */
1907: public void setContent(String content) {
1908: this .contents = content;
1909: }
1910:
1911: /**
1912: * Changes the name of the element.
1913: *
1914: * @param name
1915: * The new name.
1916: *
1917: * @deprecated Use {@link #setName(java.lang.String) setName} instead.
1918: */
1919: public void setTagName(String name) {
1920: this .setName(name);
1921: }
1922:
1923: /**
1924: * Changes the name of the element.
1925: *
1926: * @param name
1927: * The new name.
1928: *
1929: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1930: * <ul><li><code>name != null</code>
1931: * <li><code>name</code> is a valid XML identifier
1932: * </ul></dd></dl>
1933: *
1934: * @see nanoxml.XMLElement#getName()
1935: */
1936: public void setName(String name) {
1937: this .name = name;
1938: }
1939:
1940: /**
1941: * Writes the XML element to a string.
1942: *
1943: * @see nanoxml.XMLElement#write(java.io.Writer) write(Writer)
1944: */
1945: public String toString() {
1946: try {
1947: ByteArrayOutputStream out = new ByteArrayOutputStream();
1948: OutputStreamWriter writer = new OutputStreamWriter(out);
1949: this .write(writer);
1950: writer.flush();
1951: return new String(out.toByteArray());
1952: } catch (IOException e) {
1953: // Java exception handling suxx
1954: return super .toString();
1955: }
1956: }
1957:
1958: /**
1959: * Writes the XML element to a writer.
1960: *
1961: * @param writer
1962: * The writer to write the XML data to.
1963: *
1964: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
1965: * <ul><li><code>writer != null</code>
1966: * <li><code>writer</code> is not closed
1967: * </ul></dd></dl>
1968: *
1969: * @throws java.io.IOException
1970: * If the data could not be written to the writer.
1971: *
1972: * @see nanoxml.XMLElement#toString()
1973: */
1974: public void write(Writer writer)
1975: throws IOException
1976: {
1977: if (this .name == null) {
1978: this .writeEncoded(writer, this .contents);
1979: return;
1980: }
1981: writer.write('<');
1982: writer.write(this .name);
1983: if (! this .attributes.isEmpty()) {
1984: Enumeration enum = this .attributes.keys();
1985: while (enum.hasMoreElements()) {
1986: writer.write(' ');
1987: String key = (String) enum.nextElement();
1988: String value = (String) this .attributes.get(key);
1989: writer.write(key);
1990: writer.write('='); writer.write('"');
1991: this .writeEncoded(writer, value);
1992: writer.write('"');
1993: }
1994: }
1995: if ((this .contents != null) && (this .contents.length() > 0)) {
1996: writer.write('>');
1997: this .writeEncoded(writer, this .contents);
1998: writer.write('<'); writer.write('/');
1999: writer.write(this .name);
2000: writer.write('>');
2001: } else if (this .children.isEmpty()) {
2002: writer.write('/'); writer.write('>');
2003: } else {
2004: writer.write('>');
2005: Enumeration enum = this .enumerateChildren();
2006: while (enum.hasMoreElements()) {
2007: XMLElement child = (XMLElement) enum.nextElement();
2008: child.write(writer);
2009: }
2010: writer.write('<'); writer.write('/');
2011: writer.write(this .name);
2012: writer.write('>');
2013: }
2014: }
2015:
2016: /**
2017: * Writes a string encoded to a writer.
2018: *
2019: * @param writer
2020: * The writer to write the XML data to.
2021: * @param str
2022: * The string to write encoded.
2023: *
2024: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2025: * <ul><li><code>writer != null</code>
2026: * <li><code>writer</code> is not closed
2027: * <li><code>str != null</code>
2028: * </ul></dd></dl>
2029: */
2030: protected void writeEncoded(Writer writer, String str)
2031: throws IOException {
2032: for (int i = 0; i < str.length(); i += 1) {
2033: char ch = str.charAt(i);
2034: switch (ch) {
2035: case '<':
2036: writer.write('&');
2037: writer.write('l');
2038: writer.write('t');
2039: writer.write(';');
2040: break;
2041: case '>':
2042: writer.write('&');
2043: writer.write('g');
2044: writer.write('t');
2045: writer.write(';');
2046: break;
2047: case '&':
2048: writer.write('&');
2049: writer.write('a');
2050: writer.write('m');
2051: writer.write('p');
2052: writer.write(';');
2053: break;
2054: case '"':
2055: writer.write('&');
2056: writer.write('q');
2057: writer.write('u');
2058: writer.write('o');
2059: writer.write('t');
2060: writer.write(';');
2061: break;
2062: case '\'':
2063: writer.write('&');
2064: writer.write('a');
2065: writer.write('p');
2066: writer.write('o');
2067: writer.write('s');
2068: writer.write(';');
2069: break;
2070: default:
2071: int unicode = (int) ch;
2072: if ((unicode < 32) || (unicode > 126)) {
2073: writer.write('&');
2074: writer.write('#');
2075: writer.write('x');
2076: writer.write(Integer.toString(unicode, 16));
2077: writer.write(';');
2078: } else {
2079: writer.write(ch);
2080: }
2081: }
2082: }
2083: }
2084:
2085: /**
2086: * Scans an identifier from the current reader.
2087: * The scanned identifier is appended to <code>result</code>.
2088: *
2089: * @param result
2090: * The buffer in which the scanned identifier will be put.
2091: *
2092: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2093: * <ul><li><code>result != null</code>
2094: * <li>The next character read from the reader is a valid first
2095: * character of an XML identifier.
2096: * </ul></dd></dl>
2097: *
2098: * <dl><dt><b>Postconditions:</b></dt><dd>
2099: * <ul><li>The next character read from the reader won't be an identifier
2100: * character.
2101: * </ul></dd></dl><dl>
2102: */
2103: protected void scanIdentifier(StringBuffer result)
2104: throws IOException {
2105: for (;;) {
2106: char ch = this .readChar();
2107: if (((ch < 'A') || (ch > 'Z'))
2108: && ((ch < 'a') || (ch > 'z'))
2109: && ((ch < '0') || (ch > '9')) && (ch != '_')
2110: && (ch != '.') && (ch != ':') && (ch != '-')
2111: && (ch <= '\u007E')) {
2112: this .unreadChar(ch);
2113: return;
2114: }
2115: result.append(ch);
2116: }
2117: }
2118:
2119: /**
2120: * This method scans an identifier from the current reader.
2121: *
2122: * @return the next character following the whitespace.
2123: */
2124: protected char scanWhitespace() throws IOException {
2125: for (;;) {
2126: char ch = this .readChar();
2127: switch (ch) {
2128: case ' ':
2129: case '\t':
2130: case '\n':
2131: case '\r':
2132: break;
2133: default:
2134: return ch;
2135: }
2136: }
2137: }
2138:
2139: /**
2140: * This method scans an identifier from the current reader.
2141: * The scanned whitespace is appended to <code>result</code>.
2142: *
2143: * @return the next character following the whitespace.
2144: *
2145: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2146: * <ul><li><code>result != null</code>
2147: * </ul></dd></dl>
2148: */
2149: protected char scanWhitespace(StringBuffer result)
2150: throws IOException {
2151: for (;;) {
2152: char ch = this .readChar();
2153: switch (ch) {
2154: case ' ':
2155: case '\t':
2156: case '\n':
2157: result.append(ch);
2158: case '\r':
2159: break;
2160: default:
2161: return ch;
2162: }
2163: }
2164: }
2165:
2166: /**
2167: * This method scans a delimited string from the current reader.
2168: * The scanned string without delimiters is appended to
2169: * <code>string</code>.
2170: *
2171: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2172: * <ul><li><code>string != null</code>
2173: * <li>the next char read is the string delimiter
2174: * </ul></dd></dl>
2175: */
2176: protected void scanString(StringBuffer string) throws IOException {
2177: char delimiter = this .readChar();
2178: if ((delimiter != '\'') && (delimiter != '"')) {
2179: throw this .expectedInput("' or \"");
2180: }
2181: for (;;) {
2182: char ch = this .readChar();
2183: if (ch == delimiter) {
2184: return;
2185: } else if (ch == '&') {
2186: this .resolveEntity(string);
2187: } else {
2188: string.append(ch);
2189: }
2190: }
2191: }
2192:
2193: /**
2194: * Scans a #PCDATA element. CDATA sections and entities are resolved.
2195: * The next < char is skipped.
2196: * The scanned data is appended to <code>data</code>.
2197: *
2198: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2199: * <ul><li><code>data != null</code>
2200: * </ul></dd></dl>
2201: */
2202: protected void scanPCData(StringBuffer data) throws IOException {
2203: for (;;) {
2204: char ch = this .readChar();
2205: if (ch == '<') {
2206: ch = this .readChar();
2207: if (ch == '!') {
2208: this .checkCDATA(data);
2209: } else {
2210: this .unreadChar(ch);
2211: return;
2212: }
2213: } else if (ch == '&') {
2214: this .resolveEntity(data);
2215: } else {
2216: data.append(ch);
2217: }
2218: }
2219: }
2220:
2221: /**
2222: * Scans a special tag and if the tag is a CDATA section, append its
2223: * content to <code>buf</code>.
2224: *
2225: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2226: * <ul><li><code>buf != null</code>
2227: * <li>The first < has already been read.
2228: * </ul></dd></dl>
2229: */
2230: protected boolean checkCDATA(StringBuffer buf) throws IOException {
2231: char ch = this .readChar();
2232: if (ch != '[') {
2233: this .unreadChar(ch);
2234: this .skipSpecialTag(0);
2235: return false;
2236: } else if (!this .checkLiteral("CDATA[")) {
2237: this .skipSpecialTag(1); // one [ has already been read
2238: return false;
2239: } else {
2240: int delimiterCharsSkipped = 0;
2241: while (delimiterCharsSkipped < 3) {
2242: ch = this .readChar();
2243: switch (ch) {
2244: case ']':
2245: if (delimiterCharsSkipped < 2) {
2246: delimiterCharsSkipped += 1;
2247: } else {
2248: buf.append(']');
2249: buf.append(']');
2250: delimiterCharsSkipped = 0;
2251: }
2252: break;
2253: case '>':
2254: if (delimiterCharsSkipped < 2) {
2255: for (int i = 0; i < delimiterCharsSkipped; i++) {
2256: buf.append(']');
2257: }
2258: delimiterCharsSkipped = 0;
2259: buf.append('>');
2260: } else {
2261: delimiterCharsSkipped = 3;
2262: }
2263: break;
2264: default:
2265: for (int i = 0; i < delimiterCharsSkipped; i += 1) {
2266: buf.append(']');
2267: }
2268: buf.append(ch);
2269: delimiterCharsSkipped = 0;
2270: }
2271: }
2272: return true;
2273: }
2274: }
2275:
2276: /**
2277: * Skips a comment.
2278: *
2279: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2280: * <ul><li>The first <!-- has already been read.
2281: * </ul></dd></dl>
2282: */
2283: protected void skipComment() throws IOException {
2284: int dashesToRead = 2;
2285: while (dashesToRead > 0) {
2286: char ch = this .readChar();
2287: if (ch == '-') {
2288: dashesToRead -= 1;
2289: } else {
2290: dashesToRead = 2;
2291: }
2292: }
2293: if (this .readChar() != '>') {
2294: throw this .expectedInput(">");
2295: }
2296: }
2297:
2298: /**
2299: * Skips a special tag or comment.
2300: *
2301: * @param bracketLevel The number of open square brackets ([) that have
2302: * already been read.
2303: *
2304: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2305: * <ul><li>The first <! has already been read.
2306: * <li><code>bracketLevel >= 0</code>
2307: * </ul></dd></dl>
2308: */
2309: protected void skipSpecialTag(int bracketLevel) throws IOException {
2310: int tagLevel = 1; // <
2311: char stringDelimiter = '\0';
2312: if (bracketLevel == 0) {
2313: char ch = this .readChar();
2314: if (ch == '[') {
2315: bracketLevel += 1;
2316: } else if (ch == '-') {
2317: ch = this .readChar();
2318: if (ch == '[') {
2319: bracketLevel += 1;
2320: } else if (ch == ']') {
2321: bracketLevel -= 1;
2322: } else if (ch == '-') {
2323: this .skipComment();
2324: return;
2325: }
2326: }
2327: }
2328: while (tagLevel > 0) {
2329: char ch = this .readChar();
2330: if (stringDelimiter == '\0') {
2331: if ((ch == '"') || (ch == '\'')) {
2332: stringDelimiter = ch;
2333: } else if (bracketLevel <= 0) {
2334: if (ch == '<') {
2335: tagLevel += 1;
2336: } else if (ch == '>') {
2337: tagLevel -= 1;
2338: }
2339: }
2340: if (ch == '[') {
2341: bracketLevel += 1;
2342: } else if (ch == ']') {
2343: bracketLevel -= 1;
2344: }
2345: } else {
2346: if (ch == stringDelimiter) {
2347: stringDelimiter = '\0';
2348: }
2349: }
2350: }
2351: }
2352:
2353: /**
2354: * Scans the data for literal text.
2355: * Scanning stops when a character does not match or after the complete
2356: * text has been checked, whichever comes first.
2357: *
2358: * @param literal the literal to check.
2359: *
2360: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2361: * <ul><li><code>literal != null</code>
2362: * </ul></dd></dl>
2363: */
2364: protected boolean checkLiteral(String literal) throws IOException {
2365: int length = literal.length();
2366: for (int i = 0; i < length; i += 1) {
2367: if (this .readChar() != literal.charAt(i)) {
2368: return false;
2369: }
2370: }
2371: return true;
2372: }
2373:
2374: /**
2375: * Reads a character from a reader.
2376: */
2377: protected char readChar() throws IOException {
2378: if (this .charReadTooMuch != '\0') {
2379: char ch = this .charReadTooMuch;
2380: this .charReadTooMuch = '\0';
2381: return ch;
2382: } else {
2383: int i = this .reader.read();
2384: if (i < 0) {
2385: throw this .unexpectedEndOfData();
2386: } else if (i == 10) {
2387: this .parserLineNr += 1;
2388: return '\n';
2389: } else {
2390: return (char) i;
2391: }
2392: }
2393: }
2394:
2395: /**
2396: * Scans an XML element.
2397: *
2398: * @param elt The element that will contain the result.
2399: *
2400: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2401: * <ul><li>The first < has already been read.
2402: * <li><code>elt != null</code>
2403: * </ul></dd></dl>
2404: */
2405: protected void scanElement(XMLElement elt) throws IOException {
2406: StringBuffer buf = new StringBuffer();
2407: this .scanIdentifier(buf);
2408: String name = buf.toString();
2409: elt.setName(name);
2410: char ch = this .scanWhitespace();
2411: while ((ch != '>') && (ch != '/')) {
2412: buf.setLength(0);
2413: this .unreadChar(ch);
2414: this .scanIdentifier(buf);
2415: String key = buf.toString();
2416: ch = this .scanWhitespace();
2417: if (ch != '=') {
2418: throw this .expectedInput("=");
2419: }
2420: this .unreadChar(this .scanWhitespace());
2421: buf.setLength(0);
2422: this .scanString(buf);
2423: elt.setAttribute(key, buf);
2424: ch = this .scanWhitespace();
2425: }
2426: if (ch == '/') {
2427: ch = this .readChar();
2428: if (ch != '>') {
2429: throw this .expectedInput(">");
2430: }
2431: return;
2432: }
2433: buf.setLength(0);
2434: ch = this .scanWhitespace(buf);
2435: if (ch != '<') {
2436: this .unreadChar(ch);
2437: this .scanPCData(buf);
2438: } else {
2439: for (;;) {
2440: ch = this .readChar();
2441: if (ch == '!') {
2442: if (this .checkCDATA(buf)) {
2443: this .scanPCData(buf);
2444: break;
2445: } else {
2446: ch = this .scanWhitespace(buf);
2447: if (ch != '<') {
2448: this .unreadChar(ch);
2449: this .scanPCData(buf);
2450: break;
2451: }
2452: }
2453: } else {
2454: if ((ch != '/') || this .ignoreWhitespace) {
2455: buf.setLength(0);
2456: }
2457: if (ch == '/') {
2458: this .unreadChar(ch);
2459: }
2460: break;
2461: }
2462: }
2463: }
2464: if (buf.length() == 0) {
2465: while (ch != '/') {
2466: if (ch == '!') {
2467: ch = this .readChar();
2468: if (ch != '-') {
2469: throw this .expectedInput("Comment or Element");
2470: }
2471: ch = this .readChar();
2472: if (ch != '-') {
2473: throw this .expectedInput("Comment or Element");
2474: }
2475: this .skipComment();
2476: } else {
2477: this .unreadChar(ch);
2478: XMLElement child = this .createAnotherElement();
2479: this .scanElement(child);
2480: elt.addChild(child);
2481: }
2482: ch = this .scanWhitespace();
2483: if (ch != '<') {
2484: throw this .expectedInput("<");
2485: }
2486: ch = this .readChar();
2487: }
2488: this .unreadChar(ch);
2489: } else {
2490: if (this .ignoreWhitespace) {
2491: elt.setContent(buf.toString().trim());
2492: } else {
2493: elt.setContent(buf.toString());
2494: }
2495: }
2496: ch = this .readChar();
2497: if (ch != '/') {
2498: throw this .expectedInput("/");
2499: }
2500: this .unreadChar(this .scanWhitespace());
2501: if (!this .checkLiteral(name)) {
2502: throw this .expectedInput(name);
2503: }
2504: if (this .scanWhitespace() != '>') {
2505: throw this .expectedInput(">");
2506: }
2507: }
2508:
2509: /**
2510: * Resolves an entity. The name of the entity is read from the reader.
2511: * The value of the entity is appended to <code>buf</code>.
2512: *
2513: * @param buf Where to put the entity value.
2514: *
2515: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2516: * <ul><li>The first & has already been read.
2517: * <li><code>buf != null</code>
2518: * </ul></dd></dl>
2519: */
2520: protected void resolveEntity(StringBuffer buf) throws IOException {
2521: char ch = '\0';
2522: StringBuffer keyBuf = new StringBuffer();
2523: for (;;) {
2524: ch = this .readChar();
2525: if (ch == ';') {
2526: break;
2527: }
2528: keyBuf.append(ch);
2529: }
2530: String key = keyBuf.toString();
2531: if (key.charAt(0) == '#') {
2532: try {
2533: if (key.charAt(1) == 'x') {
2534: ch = (char) Integer.parseInt(key.substring(2), 16);
2535: } else {
2536: ch = (char) Integer.parseInt(key.substring(1), 10);
2537: }
2538: } catch (NumberFormatException e) {
2539: throw this .unknownEntity(key);
2540: }
2541: buf.append(ch);
2542: } else {
2543: char[] value = (char[]) this .entities.get(key);
2544: if (value == null) {
2545: throw this .unknownEntity(key);
2546: }
2547: buf.append(value);
2548: }
2549: }
2550:
2551: /**
2552: * Pushes a character back to the read-back buffer.
2553: *
2554: * @param ch The character to push back.
2555: *
2556: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2557: * <ul><li>The read-back buffer is empty.
2558: * <li><code>ch != '\0'</code>
2559: * </ul></dd></dl>
2560: */
2561: protected void unreadChar(char ch) {
2562: this .charReadTooMuch = ch;
2563: }
2564:
2565: /**
2566: * Creates a parse exception for when an invalid valueset is given to
2567: * a method.
2568: *
2569: * @param name The name of the entity.
2570: *
2571: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2572: * <ul><li><code>name != null</code>
2573: * </ul></dd></dl>
2574: */
2575: protected XMLParseException invalidValueSet(String name) {
2576: String msg = "Invalid value set (entity name = \"" + name
2577: + "\")";
2578: return new XMLParseException(this .getName(), this .parserLineNr,
2579: msg);
2580: }
2581:
2582: /**
2583: * Creates a parse exception for when an invalid value is given to a
2584: * method.
2585: *
2586: * @param name The name of the entity.
2587: * @param value The value of the entity.
2588: *
2589: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2590: * <ul><li><code>name != null</code>
2591: * <li><code>value != null</code>
2592: * </ul></dd></dl>
2593: */
2594: protected XMLParseException invalidValue(String name, String value) {
2595: String msg = "Attribute \"" + name
2596: + "\" does not contain a valid " + "value (\"" + value
2597: + "\")";
2598: return new XMLParseException(this .getName(), this .parserLineNr,
2599: msg);
2600: }
2601:
2602: /**
2603: * Creates a parse exception for when the end of the data input has been
2604: * reached.
2605: */
2606: protected XMLParseException unexpectedEndOfData() {
2607: String msg = "Unexpected end of data reached";
2608: return new XMLParseException(this .getName(), this .parserLineNr,
2609: msg);
2610: }
2611:
2612: /**
2613: * Creates a parse exception for when a syntax error occured.
2614: *
2615: * @param context The context in which the error occured.
2616: *
2617: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2618: * <ul><li><code>context != null</code>
2619: * <li><code>context.length() > 0</code>
2620: * </ul></dd></dl>
2621: */
2622: protected XMLParseException syntaxError(String context) {
2623: String msg = "Syntax error while parsing " + context;
2624: return new XMLParseException(this .getName(), this .parserLineNr,
2625: msg);
2626: }
2627:
2628: /**
2629: * Creates a parse exception for when the next character read is not
2630: * the character that was expected.
2631: *
2632: * @param charSet The set of characters (in human readable form) that was
2633: * expected.
2634: *
2635: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2636: * <ul><li><code>charSet != null</code>
2637: * <li><code>charSet.length() > 0</code>
2638: * </ul></dd></dl>
2639: */
2640: protected XMLParseException expectedInput(String charSet) {
2641: String msg = "Expected: " + charSet;
2642: return new XMLParseException(this .getName(), this .parserLineNr,
2643: msg);
2644: }
2645:
2646: /**
2647: * Creates a parse exception for when an entity could not be resolved.
2648: *
2649: * @param name The name of the entity.
2650: *
2651: * </dl><dl><dt><b>Preconditions:</b></dt><dd>
2652: * <ul><li><code>name != null</code>
2653: * <li><code>name.length() > 0</code>
2654: * </ul></dd></dl>
2655: */
2656: protected XMLParseException unknownEntity(String name) {
2657: String msg = "Unknown or invalid entity: &" + name + ";";
2658: return new XMLParseException(this.getName(), this.parserLineNr,
2659: msg);
2660: }
2661:
2662: }
|