/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2006 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others. * *See COPYING for Details * *This program is free software; you can redistribute it and/or *modify it under the terms of the GNU General Public License *as published by the Free Software Foundation; either version 2 *of the License, or (at your option) any later version. * *This program is distributed in the hope that it will be useful, *but WITHOUT ANY WARRANTY; without even the implied warranty of *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *GNU General Public License for more details. * *You should have received a copy of the GNU General Public License *along with this program; if not, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* XMLElement.java * * $Revision: 1.7.18.4.2.8 $ * $Date: 2009/08/27 20:04:10 $ * $Name: fm_060405_integration $ * * This file is part of NanoXML 2 Lite. * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved. * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the * use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in * a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. *****************************************************************************/ /* * This version of XMLElement has been *altered* for the purposes of FreeMind * toUpperCase(Locale.ENGLISH) for turkish added. */ package freemind.main; import java.io.ByteArrayOutputStream; import java.io.CharArrayReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringReader; import java.io.Writer; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Locale; import java.util.TreeMap; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * XMLElement is a representation of an XML object. The object is able to parse * XML code. * <P> * <DL> * <DT><B>Parsing XML Data</B></DT> * <DD> * You can parse XML data using the following code: * <UL> * <CODE> * XMLElement xml = new XMLElement();<BR> * FileReader reader = new FileReader("filename.xml");<BR> * xml.parseFromReader(reader); * </CODE> * </UL> * </DD> * </DL> * <DL> * <DT><B>Retrieving Attributes</B></DT> * <DD> * You can enumerate the attributes of an element using the method * {@link #enumerateAttributeNames() enumerateAttributeNames}. The attribute * values can be retrieved using the method * {@link #getStringAttribute(java.lang.String) getStringAttribute}. The * following example shows how to list the attributes of an element: * <UL> * <CODE> * XMLElement element = ...;<BR> * Enumeration enumerator = element.getAttributeNames();<BR> * while (enumerator.hasMoreElements()) {<BR> *     String key = (String) enumerator.nextElement();<BR> *     String value = element.getStringAttribute(key);<BR> *     System.out.println(key + " = " + value);<BR> * } * </CODE> * </UL> * </DD> * </DL> * <DL> * <DT><B>Retrieving Child Elements</B></DT> * <DD> * You can enumerate the children of an element using * {@link #enumerateChildren() enumerateChildren}. The number of child elements * can be retrieved using {@link #countChildren() countChildren}.</DD> * </DL> * <DL> * <DT><B>Elements Containing Character Data</B></DT> * <DD> * If an elements contains character data, like in the following example: * <UL> * <CODE> * <title>The Title</title> * </CODE> * </UL> * you can retrieve that data using the method {@link #getContent() getContent}. * </DD> * </DL> * <DL> * <DT><B>Subclassing XMLElement</B></DT> * <DD> * When subclassing XMLElement, you need to override the method * {@link #createAnotherElement() createAnotherElement} which has to return a * new copy of the receiver.</DD> * </DL> * <P> * * * * @author Marc De Scheemaecker <<A * href="mailto:cyberelf@mac.com">cyberelf@mac.com</A>> * @version $Name: fm_060405_integration $, $Revision: 1.7.18.4.2.8 $ */ public class XMLElement { public static final String XML_NODE_XHTML_CONTENT_TAG = "richcontent"; public static final String XML_NODE_XHTML_CONTENT_END_TAG_REGEXP = "<\\s*/\\s*" + XML_NODE_XHTML_CONTENT_TAG + "\\s*>"; private static Pattern sContentEndTagPattern = null; /** * Serialization serial version ID. */ static final long serialVersionUID = 6685035139346394777L; /** * Major version of NanoXML. Classes with the same major and minor version * are binary compatible. Classes with the same major version are source * compatible. If the major version is different, you may need to modify the * client source code. * * @see freemind.main.XMLElement#NANOXML_MINOR_VERSION */ public static final int NANOXML_MAJOR_VERSION = 2; /** * Minor version of NanoXML. Classes with the same major and minor version * are binary compatible. Classes with the same major version are source * compatible. If the major version is different, you may need to modify the * client source code. * * @see freemind.main.XMLElement#NANOXML_MAJOR_VERSION */ public static final int NANOXML_MINOR_VERSION = 2; /** * The attributes given to the element. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field can be empty. * <li>The field is never <code>null</code>. * <li>The keys and the values are strings. * </ul> * </dd> * </dl> */ private TreeMap attributes; /** * Child elements of the element. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field can be empty. * <li>The field is never <code>null</code>. * <li>The elements are instances of <code>XMLElement</code> or a subclass * of <code>XMLElement</code>. * </ul> * </dd> * </dl> */ private Vector children; /** * The name of the element. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field is <code>null</code> iff the element is not initialized by * either parse or setName. * <li>If the field is not <code>null</code>, it's not empty. * <li>If the field is not <code>null</code>, it contains a valid XML * identifier. * </ul> * </dd> * </dl> */ private String name; /** * The #PCDATA content of the object. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field is <code>null</code> iff the element is not a #PCDATA * element. * <li>The field can be any string, including the empty string. * </ul> * </dd> * </dl> */ private String contents; /** * fc, 17.5.06: enable buld xml writing. */ private boolean dontEncodeContents; /** * Conversion table for &...; entities. The keys are the entity names * without the & and ; delimiters. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field is never <code>null</code>. * <li>The field always contains the following associations: * "lt" => "<", "gt" => ">", * "quot" => "\"", "apos" => "'", * "amp" => "&" * <li>The keys are strings * <li>The values are char arrays * </ul> * </dd> * </dl> */ private Hashtable entities; /** * The line number where the element starts. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li><code>lineNr >= 0</code> * </ul> * </dd> * </dl> */ private int lineNr; /** * <code>true</code> if the case of the element and attribute names are case * insensitive. */ protected boolean ignoreCase; /** * <code>true</code> if the leading and trailing whitespace of #PCDATA * sections have to be ignored. */ private boolean ignoreWhitespace; /** * Character read too much. This character provides push-back functionality * to the input reader without having to use a PushbackReader. If there is * no such character, this field is '\0'. */ private char charReadTooMuch; /** * The reader provided by the caller of the parse method. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>The field is not <code>null</code> while the parse method is running. * </ul> * </dd> * </dl> */ private Reader reader; /** * The current line number in the source content. * * <dl> * <dt><b>Invariants:</b></dt> * <dd> * <ul> * <li>parserLineNr > 0 while the parse method is running. * </ul> * </dd> * </dl> */ private int parserLineNr; // OSSXP.COM: some attribute saved in .mmx file, instead of the default .mm file. // three attlist. 0: WhiteList, 1: BlackList, 2:EmList private String special_attlist[]; private void _addtoAttlist(int list, String att) { if (list >= special_attlist.length || list <0) return; this.special_attlist[list] += att; this.special_attlist[list] += ":"; return; } private boolean _isInAttlist(int list, String att) { if ( list >= special_attlist.length || list <0 || special_attlist[list].length()==0 ) { return false; } return this.special_attlist[list].contains(att+':'); } public boolean isInWhiteAttlist(String att) { // WHITE list is not NULL if ( this.special_attlist[0].length()!=0 ) { return _isInAttlist(0, att); } // BLACK list is not NULL if ( this.special_attlist[1].length()!=0 ) { return ! _isInAttlist(1, att); } // Default return true; return true; } public boolean isInBlackAttlist(String att) { // BLACK list is not NULL if ( this.special_attlist[1].length()!=0 ) { return _isInAttlist(1, att); } // WHITE list is not NULL if ( this.special_attlist[0].length()!=0 ) { return ! _isInAttlist(0, att); } // Default return false; return false; } public boolean isInEmAttlist(String att) { return _isInAttlist(2, att); } public void addtoWhiteAttlist(String att) { _addtoAttlist(0, att); } public void addtoBlackAttlist(String att) { _addtoAttlist(1, att); } public void addtoEmAttlist(String att) { _addtoAttlist(2, att); } /** * Creates and initializes a new XML element. Calling the construction is * equivalent to: * <ul> * <code>new XMLElement(new Hashtable(), false, true) * </code> * </ul> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable) * XMLElement(Hashtable) * @see freemind.main.XMLElement#XMLElement(boolean) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable,boolean) * XMLElement(Hashtable, boolean) */ public XMLElement() { this(new Hashtable(), false, true, true); } /** * Creates and initializes a new XML element. Calling the construction is * equivalent to: * <ul> * <code>new XMLElement(entities, false, true) * </code> * </ul> * * The entity conversion table. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>entities != null</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#XMLElement() * @see freemind.main.XMLElement#XMLElement(boolean) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable,boolean) * XMLElement(Hashtable, boolean) */ public XMLElement(Hashtable entities) { this(entities, false, true, true); } /** * Creates and initializes a new XML element. Calling the construction is * equivalent to: * <ul> * <code>new XMLElement(new Hashtable(), skipLeadingWhitespace, true) * </code> * </ul> * * <code>true</code> if leading and trailing whitespace in PCDATA content * has to be removed. * * </dl> * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#XMLElement() * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable) * XMLElement(Hashtable) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable,boolean) * XMLElement(Hashtable, boolean) */ public XMLElement(boolean skipLeadingWhitespace) { this(new Hashtable(), skipLeadingWhitespace, true, true); } /** * Creates and initializes a new XML element. Calling the construction is * equivalent to: * <ul> * <code>new XMLElement(entities, skipLeadingWhitespace, true) * </code> * </ul> * * The entity conversion table. <code>true</code> if leading and trailing * whitespace in PCDATA content has to be removed. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>entities != null</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#XMLElement() * @see freemind.main.XMLElement#XMLElement(boolean) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable) * XMLElement(Hashtable) */ public XMLElement(Hashtable entities, boolean skipLeadingWhitespace) { this(entities, skipLeadingWhitespace, true, true); } /** * Creates and initializes a new XML element. * * The entity conversion table. <code>true</code> if leading and trailing * whitespace in PCDATA content has to be removed. <code>true</code> if the * case of element and attribute names have to be ignored. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>entities != null</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#XMLElement() * @see freemind.main.XMLElement#XMLElement(boolean) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable) * XMLElement(Hashtable) * @see freemind.main.XMLElement#XMLElement(java.util.Hashtable,boolean) * XMLElement(Hashtable, boolean) */ public XMLElement(Hashtable entities, boolean skipLeadingWhitespace, boolean ignoreCase) { this(entities, skipLeadingWhitespace, true, ignoreCase); } /** * Creates and initializes a new XML element. * <P> * This constructor should <I>only</I> be called from * {@link #createAnotherElement() createAnotherElement} to create child * elements. * * The entity conversion table. <code>true</code> if leading and trailing * whitespace in PCDATA content has to be removed. <code>true</code> if the * basic entities need to be added to the entity list. <code>true</code> if * the case of element and attribute names have to be ignored. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>entities != null</code> * <li>if <code>fillBasicConversionTable == false</code> then * <code>entities</code> contains at least the following entries: * <code>amp</code>, <code>lt</code>, <code>gt</code>, <code>apos</code> and * <code>quot</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => 0 * <li>enumerateChildren() => empty enumeration * <li>enumeratePropertyNames() => empty enumeration * <li>getChildren() => empty vector * <li>getContent() => "" * <li>getLineNr() => 0 * <li>getName() => null * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#createAnotherElement() */ protected XMLElement(Hashtable entities, boolean skipLeadingWhitespace, boolean fillBasicConversionTable, boolean ignoreCase) { this.ignoreWhitespace = skipLeadingWhitespace; this.ignoreCase = ignoreCase; this.name = null; this.contents = ""; this.attributes = new TreeMap(); this.children = new Vector(); this.entities = entities; this.lineNr = 0; // OSSXP.COM: save some attributes out of .mm file. this.special_attlist = new String[3]; for (int i = 0; i < 3; i++) { special_attlist[i] = ""; } Enumeration enumerator = this.entities.keys(); while (enumerator.hasMoreElements()) { Object key = enumerator.nextElement(); Object value = this.entities.get(key); if (value instanceof String) { value = ((String) value).toCharArray(); this.entities.put(key, value); } } if (fillBasicConversionTable) { this.entities.put("amp", new char[] { '&' }); this.entities.put("quot", new char[] { '"' }); this.entities.put("apos", new char[] { '\'' }); this.entities.put("lt", new char[] { '<' }); this.entities.put("gt", new char[] { '>' }); } } // We may expect that some subclass would like to have its own object public Object getUserObject() { return null; } /** * Adds a child element. * * The child element to add. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>child != null</code> * <li><code>child.getName() != null</code> * <li><code>child</code> does not have a parent element * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => old.countChildren() + 1 * <li>enumerateChildren() => old.enumerateChildren() + child * <li>getChildren() => old.enumerateChildren() + child * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#countChildren() * @see freemind.main.XMLElement#enumerateChildren() * @see freemind.main.XMLElement#getChildren() * @see freemind.main.XMLElement#removeChild(XMLElement) * removeChild(XMLElement) */ public void addChild(XMLElement child) { this.children.addElement(child); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>value != null</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name * <li>getAttribute(name) => value * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getAttribute(java.lang.String) * getAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.lang.Object) getAttribute(String, Object) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) getAttribute(String, * Hashtable, String, boolean) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String) * getStringAttribute(String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.lang.String) getStringAttribute(String, String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getStringAttribute(String, Hashtable, String, boolean) */ public void setAttribute(String name, Object value) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } this.attributes.put(name, value.toString()); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object) * setAttribute} instead. */ public void addProperty(String name, Object value) { this.setAttribute(name, value); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name * <li>getIntAttribute(name) => value * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getIntAttribute(java.lang.String) * getIntAttribute(String) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, int) * getIntAttribute(String, int) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getIntAttribute(String, Hashtable, String, boolean) */ public void setIntAttribute(String name, int value) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } this.attributes.put(name, Integer.toString(value)); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * @deprecated Use {@link #setIntAttribute(java.lang.String, int) * setIntAttribute} instead. */ public void addProperty(String key, int value) { this.setIntAttribute(key, value); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name * <li>getDoubleAttribute(name) => value * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String) * getDoubleAttribute(String) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * double) getDoubleAttribute(String, double) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute(String, Hashtable, String, boolean) */ public void setDoubleAttribute(String name, double value) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } this.attributes.put(name, Double.toString(value)); } /** * Adds or modifies an attribute. * * The name of the attribute. The value of the attribute. * * @deprecated Use {@link #setDoubleAttribute(java.lang.String, double) * setDoubleAttribute} instead. */ public void addProperty(String name, double value) { this.setDoubleAttribute(name, value); } /** * Returns the number of child elements of the element. * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li><code>result >= 0</code> * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#addChild(XMLElement) addChild(XMLElement) * @see freemind.main.XMLElement#enumerateChildren() * @see freemind.main.XMLElement#getChildren() * @see freemind.main.XMLElement#removeChild(XMLElement) * removeChild(XMLElement) */ public int countChildren() { return this.children.size(); } /** * Enumerates the attribute names. * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li><code>result != null</code> * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String) * getAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.lang.Object) getAttribute(String, String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) getAttribute(String, * Hashtable, String, boolean) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String) * getStringAttribute(String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.lang.String) getStringAttribute(String, String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getStringAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String) * getIntAttribute(String) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, int) * getIntAttribute(String, int) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getIntAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String) * getDoubleAttribute(String) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * double) getDoubleAttribute(String, double) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getBooleanAttribute(java.lang.String, * java.lang.String, java.lang.String, boolean) * getBooleanAttribute(String, String, String, boolean) */ public Iterator enumerateAttributeNames() { return this.attributes.keySet().iterator(); } /** * Enumerates the attribute names. * * @deprecated Use {@link #enumerateAttributeNames() * enumerateAttributeNames} instead. */ public Iterator enumeratePropertyNames() { return this.enumerateAttributeNames(); } /** * Enumerates the child elements. * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li><code>result != null</code> * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#addChild(XMLElement) addChild(XMLElement) * @see freemind.main.XMLElement#countChildren() * @see freemind.main.XMLElement#getChildren() * @see freemind.main.XMLElement#removeChild(XMLElement) * removeChild(XMLElement) */ public Enumeration enumerateChildren() { return this.children.elements(); } /** * Returns the child elements as a Vector. It is safe to modify this Vector. * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li><code>result != null</code> * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#addChild(XMLElement) addChild(XMLElement) * @see freemind.main.XMLElement#countChildren() * @see freemind.main.XMLElement#enumerateChildren() * @see freemind.main.XMLElement#removeChild(XMLElement) * removeChild(XMLElement) */ public Vector getChildren() { try { return (Vector) this.children.clone(); } catch (Exception e) { // this never happens, however, some Java compilers are so // braindead that they require this exception clause return null; } } /** * Returns the PCDATA content of the object. If there is no such content, * <CODE>null</CODE> is returned. * * @deprecated Use {@link #getContent() getContent} instead. */ public String getContents() { return this.getContent(); } /** * Returns the PCDATA content of the object. If there is no such content, * <CODE>null</CODE> is returned. * * @see freemind.main.XMLElement#setContent(java.lang.String) * setContent(String) */ public String getContent() { return this.contents; } /** * Returns the line nr in the source data on which the element is found. * This method returns <code>0</code> there is no associated source data. * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li><code>result >= 0</code> * </ul> * </dd> * </dl> */ public int getLineNr() { return this.lineNr; } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>null</code> is returned. * * @param name * The name of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.lang.Object) getAttribute(String, Object) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) getAttribute(String, * Hashtable, String, boolean) */ public Object getAttribute(String name) { return this.getAttribute(name, null); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>defaultValue</code> is returned. * * @param name * The name of the attribute. * @param defaultValue * Key to use if the attribute is missing. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getAttribute(java.lang.String) * getAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) getAttribute(String, * Hashtable, String, boolean) */ public Object getAttribute(String name, Object defaultValue) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } Object value = this.attributes.get(name); if (value == null) { value = defaultValue; } return value; } /** * Returns an attribute by looking up a key in a hashtable. If the attribute * doesn't exist, the value corresponding to defaultKey is returned. * <P> * As an example, if valueSet contains the mapping <code>"one" => * "1"</code> and the element contains the attribute <code>attr="one"</code> * , then <code>getAttribute("attr", mapping, defaultKey, false)</code> * returns <code>"1"</code>. * * The name of the attribute. Hashtable mapping keys to values. Key to use * if the attribute is missing. <code>true</code> if literals are valid. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>valueSet</code> != null * <li>the keys of <code>valueSet</code> are strings * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getAttribute(java.lang.String) * getAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.lang.Object) getAttribute(String, Object) */ public Object getAttribute(String name, Hashtable valueSet, String defaultKey, boolean allowLiterals) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } Object key = this.attributes.get(name); Object result; if (key == null) { key = defaultKey; } result = valueSet.get(key); if (result == null) { if (allowLiterals) { result = key; } else { throw this.invalidValue(name, (String) key); } } return result; } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>null</code> is returned. * * @param name * The name of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.lang.String) getStringAttribute(String, String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getStringAttribute(String, Hashtable, String, boolean) */ public String getStringAttribute(String name) { return this.getStringAttribute(name, null); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>defaultValue</code> is returned. * * @param name * The name of the attribute. * @param defaultValue * Key to use if the attribute is missing. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getStringAttribute(java.lang.String) * getStringAttribute(String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getStringAttribute(String, Hashtable, String, boolean) */ public String getStringAttribute(String name, String defaultValue) { return (String) this.getAttribute(name, defaultValue); } /** * Returns an attribute by looking up a key in a hashtable. If the attribute * doesn't exist, the value corresponding to defaultKey is returned. * <P> * As an example, if valueSet contains the mapping <code>"one" => * "1"</code> and the element contains the attribute <code>attr="one"</code> * , then <code>getAttribute("attr", mapping, defaultKey, false)</code> * returns <code>"1"</code>. * * The name of the attribute. Hashtable mapping keys to values. Key to use * if the attribute is missing. <code>true</code> if literals are valid. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>valueSet</code> != null * <li>the keys of <code>valueSet</code> are strings * <li>the values of <code>valueSet</code> are strings * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getStringAttribute(java.lang.String) * getStringAttribute(String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.lang.String) getStringAttribute(String, String) */ public String getStringAttribute(String name, Hashtable valueSet, String defaultKey, boolean allowLiterals) { return (String) this.getAttribute(name, valueSet, defaultKey, allowLiterals); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>0</code> is returned. * * @param name * The name of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, int) * getIntAttribute(String, int) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getIntAttribute(String, Hashtable, String, boolean) */ public int getIntAttribute(String name) { return this.getIntAttribute(name, 0); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>defaultValue</code> is returned. * * @param name * The name of the attribute. * @param defaultValue * Key to use if the attribute is missing. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getIntAttribute(java.lang.String) * getIntAttribute(String) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getIntAttribute(String, Hashtable, String, boolean) */ public int getIntAttribute(String name, int defaultValue) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } String value = (String) this.attributes.get(name); if (value == null) { return defaultValue; } else { try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw this.invalidValue(name, value); } } } /** * Returns an attribute by looking up a key in a hashtable. If the attribute * doesn't exist, the value corresponding to defaultKey is returned. * <P> * As an example, if valueSet contains the mapping <code>"one" => 1</code> * and the element contains the attribute <code>attr="one"</code>, then * <code>getIntAttribute("attr", mapping, defaultKey, false)</code> returns * <code>1</code>. * * The name of the attribute. Hashtable mapping keys to values. Key to use * if the attribute is missing. <code>true</code> if literal numbers are * valid. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>valueSet</code> != null * <li>the keys of <code>valueSet</code> are strings * <li>the values of <code>valueSet</code> are Integer objects * <li><code>defaultKey</code> is either <code>null</code>, a key in * <code>valueSet</code> or an integer. * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getIntAttribute(java.lang.String) * getIntAttribute(String) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, int) * getIntAttribute(String, int) */ public int getIntAttribute(String name, Hashtable valueSet, String defaultKey, boolean allowLiteralNumbers) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } Object key = this.attributes.get(name); Integer result; if (key == null) { key = defaultKey; } try { result = (Integer) valueSet.get(key); } catch (ClassCastException e) { throw this.invalidValueSet(name); } if (result == null) { if (!allowLiteralNumbers) { throw this.invalidValue(name, (String) key); } try { result = Integer.valueOf((String) key); } catch (NumberFormatException e) { throw this.invalidValue(name, (String) key); } } return result.intValue(); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>0.0</code> is returned. * * @param name * The name of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * double) getDoubleAttribute(String, double) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute(String, Hashtable, String, boolean) */ public double getDoubleAttribute(String name) { return this.getDoubleAttribute(name, 0.); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>defaultValue</code> is returned. * * @param name * The name of the attribute. * @param defaultValue * Key to use if the attribute is missing. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String) * getDoubleAttribute(String) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute(String, Hashtable, String, boolean) */ public double getDoubleAttribute(String name, double defaultValue) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } String value = (String) this.attributes.get(name); if (value == null) { return defaultValue; } else { try { return Double.valueOf(value).doubleValue(); } catch (NumberFormatException e) { throw this.invalidValue(name, value); } } } /** * Returns an attribute by looking up a key in a hashtable. If the attribute * doesn't exist, the value corresponding to defaultKey is returned. * <P> * As an example, if valueSet contains the mapping <code>"one" => * 1.0</code> and the element contains the attribute <code>attr="one"</code> * , then * <code>getDoubleAttribute("attr", mapping, defaultKey, false)</code> * returns <code>1.0</code>. * * The name of the attribute. Hashtable mapping keys to values. Key to use * if the attribute is missing. <code>true</code> if literal numbers are * valid. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>valueSet != null</code> * <li>the keys of <code>valueSet</code> are strings * <li>the values of <code>valueSet</code> are Double objects * <li><code>defaultKey</code> is either <code>null</code>, a key in * <code>valueSet</code> or a double. * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String) * getDoubleAttribute(String) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * double) getDoubleAttribute(String, double) */ public double getDoubleAttribute(String name, Hashtable valueSet, String defaultKey, boolean allowLiteralNumbers) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } Object key = this.attributes.get(name); Double result; if (key == null) { key = defaultKey; } try { result = (Double) valueSet.get(key); } catch (ClassCastException e) { throw this.invalidValueSet(name); } if (result == null) { if (!allowLiteralNumbers) { throw this.invalidValue(name, (String) key); } try { result = Double.valueOf((String) key); } catch (NumberFormatException e) { throw this.invalidValue(name, (String) key); } } return result.doubleValue(); } /** * Returns an attribute of the element. If the attribute doesn't exist, * <code>defaultValue</code> is returned. If the value of the attribute is * equal to <code>trueValue</code>, <code>true</code> is returned. If the * value of the attribute is equal to <code>falseValue</code>, * <code>false</code> is returned. If the value doesn't match * <code>trueValue</code> or <code>falseValue</code>, an exception is * thrown. * * @param name * The name of the attribute. * @param trueValue * The value associated with <code>true</code>. * @param falseValue * The value associated with <code>true</code>. * @param defaultValue * Value to use if the attribute is missing. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * <li><code>trueValue</code> and <code>falseValue</code> are * different strings. * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#removeAttribute(java.lang.String) * removeAttribute(String) * @see freemind.main.XMLElement#enumerateAttributeNames() */ public boolean getBooleanAttribute(String name, String trueValue, String falseValue, boolean defaultValue) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } Object value = this.attributes.get(name); if (value == null) { return defaultValue; } else if (value.equals(trueValue)) { return true; } else if (value.equals(falseValue)) { return false; } else { throw this.invalidValue(name, (String) value); } } /** * Returns an attribute by looking up a key in a hashtable. * * @deprecated Use * {@link #getIntAttribute(java.lang.String, java.util.Hashtable, java.lang.String, boolean) * getIntAttribute} instead. */ public int getIntProperty(String name, Hashtable valueSet, String defaultKey) { return this.getIntAttribute(name, valueSet, defaultKey, false); } /** * Returns an attribute. * * @deprecated Use {@link #getStringAttribute(java.lang.String) * getStringAttribute} instead. */ public String getProperty(String name) { return this.getStringAttribute(name); } /** * Returns an attribute. * * @deprecated Use * {@link #getStringAttribute(java.lang.String, java.lang.String) * getStringAttribute} instead. */ public String getProperty(String name, String defaultValue) { return this.getStringAttribute(name, defaultValue); } /** * Returns an attribute. * * @deprecated Use {@link #getIntAttribute(java.lang.String, int) * getIntAttribute} instead. */ public int getProperty(String name, int defaultValue) { return this.getIntAttribute(name, defaultValue); } /** * Returns an attribute. * * @deprecated Use {@link #getDoubleAttribute(java.lang.String, double) * getDoubleAttribute} instead. */ public double getProperty(String name, double defaultValue) { return this.getDoubleAttribute(name, defaultValue); } /** * Returns an attribute. * * @deprecated Use * {@link #getBooleanAttribute(java.lang.String, java.lang.String, java.lang.String, boolean) * getBooleanAttribute} instead. */ public boolean getProperty(String key, String trueValue, String falseValue, boolean defaultValue) { return this.getBooleanAttribute(key, trueValue, falseValue, defaultValue); } /** * Returns an attribute by looking up a key in a hashtable. * * @deprecated Use * {@link #getAttribute(java.lang.String, java.util.Hashtable, java.lang.String, boolean) * getAttribute} instead. */ public Object getProperty(String name, Hashtable valueSet, String defaultKey) { return this.getAttribute(name, valueSet, defaultKey, false); } /** * Returns an attribute by looking up a key in a hashtable. * * @deprecated Use * {@link #getStringAttribute(java.lang.String, java.util.Hashtable, java.lang.String, boolean) * getStringAttribute} instead. */ public String getStringProperty(String name, Hashtable valueSet, String defaultKey) { return this.getStringAttribute(name, valueSet, defaultKey, false); } /** * Returns an attribute by looking up a key in a hashtable. * * @deprecated Use * {@link #getIntAttribute(java.lang.String, java.util.Hashtable, java.lang.String, boolean) * getIntAttribute} instead. */ public int getSpecialIntProperty(String name, Hashtable valueSet, String defaultKey) { return this.getIntAttribute(name, valueSet, defaultKey, true); } /** * Returns an attribute by looking up a key in a hashtable. * * @deprecated Use * {@link #getDoubleAttribute(java.lang.String, java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute} instead. */ public double getSpecialDoubleProperty(String name, Hashtable valueSet, String defaultKey) { return this.getDoubleAttribute(name, valueSet, defaultKey, true); } /** * Returns the name of the element. * * @see freemind.main.XMLElement#setName(java.lang.String) setName(String) */ public String getName() { return this.name; } /** * Returns the name of the element. * * @deprecated Use {@link #getName() getName} instead. */ public String getTagName() { return this.getName(); } /** * Reads one XML element from a java.io.Reader and parses it. * * The reader from which to retrieve the XML data. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>reader != null</code> * <li><code>reader</code> is not closed * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * <li>the reader points to the first character following the last '>' * character of the XML element * </ul> * </dd> * </dl> * <dl> * * @throws java.io.IOException * If an error occured while reading the input. * @throws XMLParseException * If an error occured while parsing the read data. */ public void parseFromReader(Reader reader) throws IOException, XMLParseException { this.parseFromReader(reader, /* startingLineNr */1); } /** * Reads one XML element from a java.io.Reader and parses it. * * The reader from which to retrieve the XML data. The line number of the * first line in the data. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>reader != null</code> * <li><code>reader</code> is not closed * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * <li>the reader points to the first character following the last '>' * character of the XML element * </ul> * </dd> * </dl> * <dl> * * @throws java.io.IOException * If an error occured while reading the input. * @throws XMLParseException * If an error occured while parsing the read data. */ public void parseFromReader(Reader reader, int startingLineNr) throws IOException, XMLParseException { this.charReadTooMuch = '\0'; this.reader = reader; this.parserLineNr = startingLineNr; for (;;) { char ch = this.scanWhitespace(); if (ch != '<') { throw this.expectedInput("<"); } ch = this.readChar(); if ((ch == '!') || (ch == '?')) { this.skipSpecialTag(0); } else { this.unreadChar(ch); this.scanElement(this); return; } } } /** * Reads one XML element from a String and parses it. * * The reader from which to retrieve the XML data. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>string != null</code> * <li><code>string.length() > 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseString(String string) throws XMLParseException { try { this.parseFromReader(new StringReader(string), /* startingLineNr */1); } catch (IOException e) { // Java exception handling suxx } } /** * Reads one XML element from a String and parses it. * * The reader from which to retrieve the XML data. The first character in * <code>string</code> to scan. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>string != null</code> * <li><code>offset < string.length()</code> * <li><code>offset >= 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseString(String string, int offset) throws XMLParseException { this.parseString(string.substring(offset)); } /** * Reads one XML element from a String and parses it. * * The reader from which to retrieve the XML data. The first character in * <code>string</code> to scan. The character where to stop scanning. This * character is not scanned. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>string != null</code> * <li><code>end <= string.length()</code> * <li><code>offset < end</code> * <li><code>offset >= 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseString(String string, int offset, int end) throws XMLParseException { this.parseString(string.substring(offset, end)); } /** * Reads one XML element from a String and parses it. * * The reader from which to retrieve the XML data. The first character in * <code>string</code> to scan. The character where to stop scanning. This * character is not scanned. The line number of the first line in the data. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>string != null</code> * <li><code>end <= string.length()</code> * <li><code>offset < end</code> * <li><code>offset >= 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseString(String string, int offset, int end, int startingLineNr) throws XMLParseException { string = string.substring(offset, end); try { this.parseFromReader(new StringReader(string), startingLineNr); } catch (IOException e) { // Java exception handling suxx } } /** * Reads one XML element from a char array and parses it. * * The reader from which to retrieve the XML data. The first character in * <code>string</code> to scan. The character where to stop scanning. This * character is not scanned. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>input != null</code> * <li><code>end <= input.length</code> * <li><code>offset < end</code> * <li><code>offset >= 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseCharArray(char[] input, int offset, int end) throws XMLParseException { this.parseCharArray(input, offset, end, /* startingLineNr */1); } /** * Reads one XML element from a char array and parses it. * * The reader from which to retrieve the XML data. The first character in * <code>string</code> to scan. The character where to stop scanning. This * character is not scanned. The line number of the first line in the data. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>input != null</code> * <li><code>end <= input.length</code> * <li><code>offset < end</code> * <li><code>offset >= 0</code> * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>the state of the receiver is updated to reflect the XML element * parsed from the reader * </ul> * </dd> * </dl> * <dl> * * @throws XMLParseException * If an error occured while parsing the string. */ public void parseCharArray(char[] input, int offset, int end, int startingLineNr) throws XMLParseException { try { Reader reader = new CharArrayReader(input, offset, end); this.parseFromReader(reader, startingLineNr); } catch (IOException e) { // This exception will never happen. } } /** * Removes a child element. * * The child element to remove. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>child != null</code> * <li><code>child</code> is a child element of the receiver * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>countChildren() => old.countChildren() - 1 * <li>enumerateChildren() => old.enumerateChildren() - child * <li>getChildren() => old.enumerateChildren() - child * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#addChild(XMLElement) addChild(XMLElement) * @see freemind.main.XMLElement#countChildren() * @see freemind.main.XMLElement#enumerateChildren() * @see freemind.main.XMLElement#getChildren() */ public void removeChild(XMLElement child) { this.children.removeElement(child); } /** * Removes an attribute. * * The name of the attribute. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>enumerateAttributeNames() => old.enumerateAttributeNames() - name * <li>getAttribute(name) => <code>null</code> * </ul> * </dd> * </dl> * <dl> * * @see freemind.main.XMLElement#enumerateAttributeNames() * @see freemind.main.XMLElement#setDoubleAttribute(java.lang.String, * double) setDoubleAttribute(String, double) * @see freemind.main.XMLElement#setIntAttribute(java.lang.String, int) * setIntAttribute(String, int) * @see freemind.main.XMLElement#setAttribute(java.lang.String, * java.lang.Object) setAttribute(String, Object) * @see freemind.main.XMLElement#getAttribute(java.lang.String) * getAttribute(String) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.lang.Object) getAttribute(String, Object) * @see freemind.main.XMLElement#getAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) getAttribute(String, * Hashtable, String, boolean) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String) * getStringAttribute(String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.lang.String) getStringAttribute(String, String) * @see freemind.main.XMLElement#getStringAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getStringAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String) * getIntAttribute(String) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, int) * getIntAttribute(String, int) * @see freemind.main.XMLElement#getIntAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getIntAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String) * getDoubleAttribute(String) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * double) getDoubleAttribute(String, double) * @see freemind.main.XMLElement#getDoubleAttribute(java.lang.String, * java.util.Hashtable, java.lang.String, boolean) * getDoubleAttribute(String, Hashtable, String, boolean) * @see freemind.main.XMLElement#getBooleanAttribute(java.lang.String, * java.lang.String, java.lang.String, boolean) * getBooleanAttribute(String, String, String, boolean) */ public void removeAttribute(String name) { if (this.ignoreCase) { name = name.toUpperCase(Locale.ENGLISH); } this.attributes.remove(name); } /** * Removes an attribute. * * The name of the attribute. * * @deprecated Use {@link #removeAttribute(java.lang.String) * removeAttribute} instead. */ public void removeProperty(String name) { this.removeAttribute(name); } /** * Removes an attribute. * * The name of the attribute. * * @deprecated Use {@link #removeAttribute(java.lang.String) * removeAttribute} instead. */ public void removeChild(String name) { this.removeAttribute(name); } /** * Creates a new similar XML element. * <P> * You should override this method when subclassing XMLElement. */ protected XMLElement createAnotherElement() { return new XMLElement(this.entities, this.ignoreWhitespace, false, this.ignoreCase); } // You should override this method when subclassing XMLElement. protected void completeElement() { } /** * Changes the content string. * * The new content string. */ public void setContent(String content) { this.contents = content; this.dontEncodeContents = false; } /** * Changes the content string. * * The new content string. */ public void setEncodedContent(String content) { this.contents = content; this.dontEncodeContents = true; } /** * Changes the name of the element. * * The new name. * * @deprecated Use {@link #setName(java.lang.String) setName} instead. */ public void setTagName(String name) { this.setName(name); } /** * Changes the name of the element. * * The new name. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name</code> is a valid XML identifier * </ul> * </dd> * </dl> * * @see freemind.main.XMLElement#getName() */ public void setName(String name) { this.name = name; } /** * Writes the XML element to a string. * * @see freemind.main.XMLElement#write(java.io.Writer) write(Writer) */ public String toString() { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); // OSSXP.COM: Encode output to default_charset(UTF-8). OutputStreamWriter writer = new OutputStreamWriter(out, FreeMind.DEFAULT_CHARSET); this.write(writer); writer.flush(); return new String(out.toByteArray()); } catch (IOException e) { // Java exception handling suxx return super.toString(); } } /** * Writes the XML element to a writer. * * The writer to write the XML data to. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>writer != null</code> * <li><code>writer</code> is not closed * </ul> * </dd> * </dl> * * @throws java.io.IOException * If the data could not be written to the writer. * * @see freemind.main.XMLElement#toString() */ public void write(Writer writer) throws IOException { write(writer, true); } public void writeWithoutClosingTag(Writer writer) throws IOException { write(writer, false); } public void writeClosingTag(Writer writer) throws IOException { writer.write('<'); writer.write('/'); writer.write(this.name); writer.write('>'); writer.write('\n'); } public void write(Writer writer, boolean withClosingTag) throws IOException { if (this.name == null) { // fc, 17.5.06: support encoded contents if (dontEncodeContents) { writer.write(this.contents); } else { this.writeEncoded(writer, this.contents); } return; } writer.write('<'); writer.write(this.name); if (!this.attributes.isEmpty()) { Iterator enumerator = this.attributes.keySet().iterator(); while (enumerator.hasNext()) { String key = (String) enumerator.next(); String value = (String) this.attributes.get(key); // OSSXP.COM: if ( this.isInBlackAttlist(key) ) { continue; } writer.write(' '); if ( this.isInEmAttlist(key) ) { writer.write("\n\t"); } writer.write(key); writer.write('='); writer.write('"'); this.writeEncoded(writer, value); writer.write('"'); } } if ((this.contents != null) && (this.contents.length() > 0)) { writer.write('>'); // writer.write('\n'); // fc, 17.5.06: support encoded contents if (dontEncodeContents) { writer.write(this.contents); } else { this.writeEncoded(writer, this.contents); } if (withClosingTag) { writer.write('<'); writer.write('/'); writer.write(this.name); writer.write('>'); writer.write('\n'); } } else if (this.children.isEmpty()) { if (withClosingTag) { writer.write('/'); } writer.write('>'); writer.write('\n'); } else { writer.write('>'); writer.write('\n'); Enumeration enumerator = this.enumerateChildren(); while (enumerator.hasMoreElements()) { XMLElement child = (XMLElement) enumerator.nextElement(); child.write(writer); } if (withClosingTag) { writer.write('<'); writer.write('/'); writer.write(this.name); writer.write('>'); writer.write('\n'); } } } /** * Writes a string encoded to a writer. * * The writer to write the XML data to. The string to write encoded. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>writer != null</code> * <li><code>writer</code> is not closed * <li><code>str != null</code> * </ul> * </dd> * </dl> */ protected void writeEncoded(Writer writer, String str) throws IOException { for (int i = 0; i < str.length(); i += 1) { char ch = str.charAt(i); switch (ch) { case '<': writer.write('&'); writer.write('l'); writer.write('t'); writer.write(';'); break; case '>': writer.write('&'); writer.write('g'); writer.write('t'); writer.write(';'); break; case '&': writer.write('&'); writer.write('a'); writer.write('m'); writer.write('p'); writer.write(';'); break; case '"': writer.write('&'); writer.write('q'); writer.write('u'); writer.write('o'); writer.write('t'); writer.write(';'); break; case '\'': writer.write('&'); writer.write('a'); writer.write('p'); writer.write('o'); writer.write('s'); writer.write(';'); break; default: int unicode = (int) ch; // OSSXP.COM: do not convert Chinese characters into &#blahblah; if (!Resources.getInstance().getBoolProperty("wh_nonascii_in_utf8") && (unicode > 126) || (unicode < 32)) { writer.write('&'); writer.write('#'); writer.write('x'); writer.write(Integer.toString(unicode, 16)); writer.write(';'); } else { writer.write(ch); } } } } /** * Scans an identifier from the current reader. The scanned identifier is * appended to <code>result</code>. * * The buffer in which the scanned identifier will be put. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>result != null</code> * <li>The next character read from the reader is a valid first character of * an XML identifier. * </ul> * </dd> * </dl> * * <dl> * <dt><b>Postconditions:</b></dt> * <dd> * <ul> * <li>The next character read from the reader won't be an identifier * character. * </ul> * </dd> * </dl> * <dl> */ protected void scanIdentifier(StringBuffer result) throws IOException { for (;;) { char ch = this.readChar(); if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z')) && ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.') && (ch != ':') && (ch != '-') && (ch <= '\u007E')) { this.unreadChar(ch); return; } result.append(ch); } } /** * This method scans an identifier from the current reader. * * @return the next character following the whitespace. */ protected char scanWhitespace() throws IOException { for (;;) { char ch = this.readChar(); switch (ch) { case ' ': case '\t': case '\n': case '\r': break; default: return ch; } } } /** * This method scans an identifier from the current reader. The scanned * whitespace is appended to <code>result</code>. * * @return the next character following the whitespace. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>result != null</code> * </ul> * </dd> * </dl> */ protected char scanWhitespace(StringBuffer result) throws IOException { for (;;) { char ch = this.readChar(); switch (ch) { case ' ': case '\t': case '\n': result.append(ch); case '\r': break; default: return ch; } } } /** * This method scans a delimited string from the current reader. The scanned * string without delimiters is appended to <code>string</code>. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>string != null</code> * <li>the next char read is the string delimiter * </ul> * </dd> * </dl> */ protected void scanString(StringBuffer string) throws IOException { char delimiter = this.readChar(); if ((delimiter != '\'') && (delimiter != '"')) { throw this.expectedInput("' or \""); } for (;;) { char ch = this.readChar(); if (ch == delimiter) { return; } else if (ch == '&') { this.resolveEntity(string); } else { string.append(ch); } } } /** * Scans a #PCDATA element. CDATA sections and entities are resolved. The * next < char is skipped. The scanned data is appended to * <code>data</code>. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>data != null</code> * </ul> * </dd> * </dl> */ protected void scanPCData(StringBuffer data) throws IOException { for (;;) { char ch = this.readChar(); if (ch == '<') { ch = this.readChar(); if (ch == '!') { this.checkCDATA(data); } else { this.unreadChar(ch); return; } } else if (ch == '&') { this.resolveEntity(data); } else { data.append(ch); } } } /** * Scans a special tag and if the tag is a CDATA section, append its content * to <code>buf</code>. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>buf != null</code> * <li>The first < has already been read. * </ul> * </dd> * </dl> */ protected boolean checkCDATA(StringBuffer buf) throws IOException { char ch = this.readChar(); if (ch != '[') { this.unreadChar(ch); this.skipSpecialTag(0); return false; } else if (!this.checkLiteral("CDATA[")) { this.skipSpecialTag(1); // one [ has already been read return false; } else { int delimiterCharsSkipped = 0; while (delimiterCharsSkipped < 3) { ch = this.readChar(); switch (ch) { case ']': if (delimiterCharsSkipped < 2) { delimiterCharsSkipped += 1; } else { buf.append(']'); buf.append(']'); delimiterCharsSkipped = 0; } break; case '>': if (delimiterCharsSkipped < 2) { for (int i = 0; i < delimiterCharsSkipped; i++) { buf.append(']'); } delimiterCharsSkipped = 0; buf.append('>'); } else { delimiterCharsSkipped = 3; } break; default: for (int i = 0; i < delimiterCharsSkipped; i += 1) { buf.append(']'); } buf.append(ch); delimiterCharsSkipped = 0; } } return true; } } /** * Skips a comment. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li>The first <!-- has already been read. * </ul> * </dd> * </dl> */ protected void skipComment() throws IOException { int dashesToRead = 2; while (dashesToRead > 0) { char ch = this.readChar(); if (ch == '-') { dashesToRead -= 1; } else { dashesToRead = 2; } } if (this.readChar() != '>') { throw this.expectedInput(">"); } } /** * Skips a special tag or comment. * * @param bracketLevel * The number of open square brackets ([) that have already been * read. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li>The first <! has already been read. * <li><code>bracketLevel >= 0</code> * </ul> * </dd> * </dl> */ protected void skipSpecialTag(int bracketLevel) throws IOException { int tagLevel = 1; // < char stringDelimiter = '\0'; if (bracketLevel == 0) { char ch = this.readChar(); if (ch == '[') { bracketLevel += 1; } else if (ch == '-') { ch = this.readChar(); if (ch == '[') { bracketLevel += 1; } else if (ch == ']') { bracketLevel -= 1; } else if (ch == '-') { this.skipComment(); return; } } } while (tagLevel > 0) { char ch = this.readChar(); if (stringDelimiter == '\0') { if ((ch == '"') || (ch == '\'')) { stringDelimiter = ch; } else if (bracketLevel <= 0) { if (ch == '<') { tagLevel += 1; } else if (ch == '>') { tagLevel -= 1; } } if (ch == '[') { bracketLevel += 1; } else if (ch == ']') { bracketLevel -= 1; } } else { if (ch == stringDelimiter) { stringDelimiter = '\0'; } } } } /** * Scans the data for literal text. Scanning stops when a character does not * match or after the complete text has been checked, whichever comes first. * * @param literal * the literal to check. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>literal != null</code> * </ul> * </dd> * </dl> */ protected boolean checkLiteral(String literal) throws IOException { int length = literal.length(); for (int i = 0; i < length; i += 1) { if (this.readChar() != literal.charAt(i)) { return false; } } return true; } /** * Reads a character from a reader. */ protected char readChar() throws IOException { if (this.charReadTooMuch != '\0') { char ch = this.charReadTooMuch; this.charReadTooMuch = '\0'; return ch; } else { int i = this.reader.read(); if (i < 0) { throw this.unexpectedEndOfData(); } else if (i == 10) { this.parserLineNr += 1; return '\n'; } else { return (char) i; } } } /** * Scans an XML element. * * @param elt * The element that will contain the result. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li>The first < has already been read. * <li><code>elt != null</code> * </ul> * </dd> * </dl> */ protected void scanElement(XMLElement elt) throws IOException { boolean isCollectionMode = false; StringBuffer buf = new StringBuffer(); this.scanIdentifier(buf); String name = buf.toString(); elt.setName(name); if (XML_NODE_XHTML_CONTENT_TAG.equals(name)) { /* * special case of html content tag: collect chars until </...> * occurs. */ isCollectionMode = true; } char ch = this.scanWhitespace(); // Scan the attributes of opening tag while ((ch != '>') && (ch != '/')) { // Not the end of the tag buf.setLength(0); this.unreadChar(ch); this.scanIdentifier(buf); String key = buf.toString(); ch = this.scanWhitespace(); if (ch != '=') { throw this.expectedInput("="); } this.unreadChar(this.scanWhitespace()); buf.setLength(0); this.scanString(buf); elt.setAttribute(key, buf); ch = this.scanWhitespace(); } if (ch == '/') { // Case of self ending tag ch = this.readChar(); if (ch != '>') { throw this.expectedInput(">"); } elt.completeElement(); return; } // special collection mode: if (isCollectionMode) { StringBuffer waitingBuf = new StringBuffer(); int lastOpeningBreak = -1; for (;;) { ch = this.readChar(); waitingBuf.append(ch); if (ch == '<') { lastOpeningBreak = waitingBuf.length() - 1; } if (ch == '>' && lastOpeningBreak >= 0) { String content = waitingBuf.toString(); if (sContentEndTagPattern == null) { sContentEndTagPattern = Pattern .compile(XML_NODE_XHTML_CONTENT_END_TAG_REGEXP); } String substring = content.substring(lastOpeningBreak); Matcher matcher = sContentEndTagPattern.matcher(substring); if (matcher.matches()) { // end found, remove the end tag: content = content.substring(0, lastOpeningBreak); // Dimitry: begin // // PCDATA // if (this.ignoreWhitespace) { // elt.setContent(content.trim()); // } else { // elt.setContent(content); // } // Dimitry: always remove spaces around the rich content // block because it should be valid xml. elt.setContent(content.trim()); // Dimitry: end elt.completeElement(); return; } } } } // This part is unclear - probing for PCDATA buf.setLength(0); ch = this.scanWhitespace(buf); if (ch != '<') { // Either: PCDATA this.unreadChar(ch); this.scanPCData(buf); } else { // Or: Maybe sequence of children tags for (;;) { // This is a loop, after all ch = this.readChar(); if (ch == '!') { if (this.checkCDATA(buf)) { this.scanPCData(buf); break; } else { ch = this.scanWhitespace(buf); if (ch != '<') { this.unreadChar(ch); this.scanPCData(buf); break; } } } else { buf.setLength(0); break; } } } if (buf.length() == 0) { // Not PCDATA, '<' already read while (ch != '/') { if (ch == '!') { // Comment ch = this.readChar(); if (ch != '-') { throw this.expectedInput("Comment or Element"); } ch = this.readChar(); if (ch != '-') { throw this.expectedInput("Comment or Element"); } this.skipComment(); } else { // Not Comment this.unreadChar(ch); XMLElement child = this.createAnotherElement(); // Here goes the recursion. this.scanElement(child); elt.addChild(child); } ch = this.scanWhitespace(); if (ch != '<') { throw this.expectedInput("<"); } ch = this.readChar(); } this.unreadChar(ch); } else { // PCDATA if (this.ignoreWhitespace) { elt.setContent(buf.toString().trim()); } else { elt.setContent(buf.toString()); } } // ch = this.readChar(); if (ch != '/') { throw this.expectedInput("/"); } this.unreadChar(this.scanWhitespace()); if (!this.checkLiteral(name)) { throw this.expectedInput(name); } if (this.scanWhitespace() != '>') { throw this.expectedInput(">"); } elt.completeElement(); } /** * Resolves an entity. The name of the entity is read from the reader. The * value of the entity is appended to <code>buf</code>. * * @param buf * Where to put the entity value. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li>The first & has already been read. * <li><code>buf != null</code> * </ul> * </dd> * </dl> */ protected void resolveEntity(StringBuffer buf) throws IOException { char ch = '\0'; StringBuffer keyBuf = new StringBuffer(); for (;;) { ch = this.readChar(); if (ch == ';') { break; } keyBuf.append(ch); } String key = keyBuf.toString(); if (key.charAt(0) == '#') { try { if (key.charAt(1) == 'x') { ch = (char) Integer.parseInt(key.substring(2), 16); } else { ch = (char) Integer.parseInt(key.substring(1), 10); } } catch (NumberFormatException e) { throw this.unknownEntity(key); } buf.append(ch); } else { char[] value = (char[]) this.entities.get(key); if (value == null) { throw this.unknownEntity(key); } buf.append(value); } } /** * Pushes a character back to the read-back buffer. * * @param ch * The character to push back. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li>The read-back buffer is empty. * <li><code>ch != '\0'</code> * </ul> * </dd> * </dl> */ protected void unreadChar(char ch) { this.charReadTooMuch = ch; } /** * Creates a parse exception for when an invalid valueset is given to a * method. * * @param name * The name of the entity. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * </ul> * </dd> * </dl> */ protected XMLParseException invalidValueSet(String name) { String msg = "Invalid value set (entity name = \"" + name + "\")"; return new XMLParseException(this.getName(), this.parserLineNr, msg); } /** * Creates a parse exception for when an invalid value is given to a method. * * @param name * The name of the entity. * @param value * The value of the entity. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>value != null</code> * </ul> * </dd> * </dl> */ protected XMLParseException invalidValue(String name, String value) { String msg = "Attribute \"" + name + "\" does not contain a valid " + "value (\"" + value + "\")"; return new XMLParseException(this.getName(), this.parserLineNr, msg); } /** * Creates a parse exception for when the end of the data input has been * reached. */ protected XMLParseException unexpectedEndOfData() { String msg = "Unexpected end of data reached"; return new XMLParseException(this.getName(), this.parserLineNr, msg); } /** * Creates a parse exception for when a syntax error occured. * * @param context * The context in which the error occured. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>context != null</code> * <li><code>context.length() > 0</code> * </ul> * </dd> * </dl> */ protected XMLParseException syntaxError(String context) { String msg = "Syntax error while parsing " + context; return new XMLParseException(this.getName(), this.parserLineNr, msg); } /** * Creates a parse exception for when the next character read is not the * character that was expected. * * @param charSet * The set of characters (in human readable form) that was * expected. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>charSet != null</code> * <li><code>charSet.length() > 0</code> * </ul> * </dd> * </dl> */ protected XMLParseException expectedInput(String charSet) { String msg = "Expected: " + charSet; return new XMLParseException(this.getName(), this.parserLineNr, msg); } /** * Creates a parse exception for when an entity could not be resolved. * * @param name * The name of the entity. * * </dl> * <dl> * <dt><b>Preconditions:</b></dt> * <dd> * <ul> * <li><code>name != null</code> * <li><code>name.length() > 0</code> * </ul> * </dd> * </dl> */ protected XMLParseException unknownEntity(String name) { String msg = "Unknown or invalid entity: &" + name + ";"; return new XMLParseException(this.getName(), this.parserLineNr, msg); } }