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