# HG changeset patch # User Jaroslav Tulach # Date 1245858719 -7200 # Node ID 75acc009e9d8baa91a0659be5059a9f595b71f3b # Parent 6dcea85ad8ba5a3133c8fd067bd9c62df73fb50e# Parent 601d21ee9aa6082e925f479464b514dad8ea2d75 Eliminating need for XML SAX and DOM 2 in base Java module diff -r 6dcea85ad8ba -r 75acc009e9d8 build.xml --- a/build.xml Mon Jun 22 18:07:11 2009 +0200 +++ b/build.xml Wed Jun 24 17:51:59 2009 +0200 @@ -4,15 +4,19 @@ + + + + - + @@ -59,7 +63,13 @@ - + + + + + + + + + + + + + + + + + + + + + + @@ -243,6 +288,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -333,6 +417,8 @@ includejavaruntime="false" includeantruntime="false" classpath="${module.cp}:${plugs}/jre/lib/rt-closed.jar" + debug="true" + debuglevel="lines,vars,source" > diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/META-INF/services/sun.util.xml.PrefsXMLSupport --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/META-INF/services/sun.util.xml.PrefsXMLSupport Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,1 @@ +com.sun.xml.internal.PrefsXmlSupportImpl diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/META-INF/services/sun.util.xml.PropertiesXMLUtils --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/META-INF/services/sun.util.xml.PropertiesXMLUtils Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,1 @@ +com.sun.xml.internal.PropertiesXMLUtilsImpl diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/com/sun/xml/internal/PrefsXmlSupportImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/xml/internal/PrefsXmlSupportImpl.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,439 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.xml.internal; + +import java.util.*; +import java.util.prefs.*; +import java.io.*; +import javax.xml.parsers.*; +import javax.xml.transform.*; +import javax.xml.transform.dom.*; +import javax.xml.transform.stream.*; +import org.xml.sax.*; +import org.w3c.dom.*; + +/** + * XML Support for java.util.prefs. Methods to import and export preference + * nodes and subtrees. + * + * @author Josh Bloch and Mark Reinhold + * @see Preferences + * @since 1.4 + */ +public class PrefsXmlSupportImpl extends sun.util.xml.PrefsXmlSupport { + // The required DTD URI for exported preferences + private static final String PREFS_DTD_URI = + "http://java.sun.com/dtd/preferences.dtd"; + + // The actual DTD corresponding to the URI + private static final String PREFS_DTD = + "" + + + "" + + + "" + + "" + + + "" + + "" + + + "" + + "" + + + "" + + "" + + "" + + "" ; + /** + * Version number for the format exported preferences files. + */ + private static final String EXTERNAL_XML_VERSION = "1.0"; + + /* + * Version number for the internal map files. + */ + private static final String MAP_XML_VERSION = "1.0"; + + /** + * Export the specified preferences node and, if subTree is true, all + * subnodes, to the specified output stream. Preferences are exported as + * an XML document conforming to the definition in the Preferences spec. + * + * @throws IOException if writing to the specified output stream + * results in an IOException. + * @throws BackingStoreException if preference data cannot be read from + * backing store. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()} method. + */ + public void export(OutputStream os, final Preferences p, boolean subTree) + throws IOException, BackingStoreException { + if (isRemoved(p)) + throw new IllegalStateException("Node has been removed"); + Document doc = createPrefsDoc("preferences"); + Element preferences = doc.getDocumentElement() ; + preferences.setAttribute("EXTERNAL_XML_VERSION", EXTERNAL_XML_VERSION); + Element xmlRoot = (Element) + preferences.appendChild(doc.createElement("root")); + xmlRoot.setAttribute("type", (p.isUserNode() ? "user" : "system")); + + // Get bottom-up list of nodes from p to root, excluding root + List ancestors = new ArrayList(); + + for (Preferences kid = p, dad = kid.parent(); dad != null; + kid = dad, dad = kid.parent()) { + ancestors.add(kid); + } + Element e = xmlRoot; + for (int i=ancestors.size()-1; i >= 0; i--) { + e.appendChild(doc.createElement("map")); + e = (Element) e.appendChild(doc.createElement("node")); + e.setAttribute("name", ((Preferences)ancestors.get(i)).name()); + } + putPreferencesInXml(e, doc, p, subTree); + + writeDoc(doc, os); + } + + /** + * Put the preferences in the specified Preferences node into the + * specified XML element which is assumed to represent a node + * in the specified XML document which is assumed to conform to + * PREFS_DTD. If subTree is true, create children of the specified + * XML node conforming to all of the children of the specified + * Preferences node and recurse. + * + * @throws BackingStoreException if it is not possible to read + * the preferences or children out of the specified + * preferences node. + */ + private static void putPreferencesInXml(Element elt, Document doc, + Preferences prefs, boolean subTree) throws BackingStoreException + { + Preferences[] kidsCopy = null; + String[] kidNames = null; + + // Node is locked to export its contents and get a + // copy of children, then lock is released, + // and, if subTree = true, recursive calls are made on children + synchronized (lock(prefs)) { + // Check if this node was concurrently removed. If yes + // remove it from XML Document and return. + if (isRemoved(prefs)) { + elt.getParentNode().removeChild(elt); + return; + } + // Put map in xml element + String[] keys = prefs.keys(); + Element map = (Element) elt.appendChild(doc.createElement("map")); + for (int i=0; iIOException. + * @throws InvalidPreferencesFormatException Data on input stream does not + * constitute a valid XML document with the mandated document type. + */ + public void importPreferences(InputStream is) + throws IOException, InvalidPreferencesFormatException + { + try { + Document doc = loadPrefsDoc(is); + String xmlVersion = + doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION"); + if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) + throw new InvalidPreferencesFormatException( + "Exported preferences file format version " + xmlVersion + + " is not supported. This java installation can read" + + " versions " + EXTERNAL_XML_VERSION + " or older. You may need" + + " to install a newer version of JDK."); + + Element xmlRoot = (Element) doc.getDocumentElement(). + getChildNodes().item(0); + Preferences prefsRoot = + (xmlRoot.getAttribute("type").equals("user") ? + Preferences.userRoot() : Preferences.systemRoot()); + ImportSubtree(prefsRoot, xmlRoot); + } catch(SAXException e) { + throw new InvalidPreferencesFormatException(e); + } + } + + /** + * Create a new prefs XML document. + */ + private static Document createPrefsDoc( String qname ) { + try { + DOMImplementation di = DocumentBuilderFactory.newInstance(). + newDocumentBuilder().getDOMImplementation(); + DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI); + return di.createDocument(null, qname, dt); + } catch(ParserConfigurationException e) { + throw new AssertionError(e); + } + } + + /** + * Load an XML document from specified input stream, which must + * have the requisite DTD URI. + */ + private static Document loadPrefsDoc(InputStream in) + throws SAXException, IOException + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setIgnoringElementContentWhitespace(true); + dbf.setValidating(true); + dbf.setCoalescing(true); + dbf.setIgnoringComments(true); + try { + DocumentBuilder db = dbf.newDocumentBuilder(); + db.setEntityResolver(new Resolver()); + db.setErrorHandler(new EH()); + return db.parse(new InputSource(in)); + } catch (ParserConfigurationException e) { + throw new AssertionError(e); + } + } + + /** + * Write XML document to the specified output stream. + */ + private static final void writeDoc(Document doc, OutputStream out) + throws IOException + { + try { + TransformerFactory tf = TransformerFactory.newInstance(); + try { + tf.setAttribute("indent-number", new Integer(2)); + } catch (IllegalArgumentException iae) { + //Ignore the IAE. Should not fail the writeout even the + //transformer provider does not support "indent-number". + } + Transformer t = tf.newTransformer(); + t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); + t.setOutputProperty(OutputKeys.INDENT, "yes"); + //Transformer resets the "indent" info if the "result" is a StreamResult with + //an OutputStream object embedded, creating a Writer object on top of that + //OutputStream object however works. + t.transform(new DOMSource(doc), + new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")))); + } catch(TransformerException e) { + throw new AssertionError(e); + } + } + + /** + * Recursively traverse the specified preferences node and store + * the described preferences into the system or current user + * preferences tree, as appropriate. + */ + private static void ImportSubtree(Preferences prefsNode, Element xmlNode) { + NodeList xmlKids = xmlNode.getChildNodes(); + int numXmlKids = xmlKids.getLength(); + /* + * We first lock the node, import its contents and get + * child nodes. Then we unlock the node and go to children + * Since some of the children might have been concurrently + * deleted we check for this. + */ + Preferences[] prefsKids; + /* Lock the node */ + synchronized (lock(prefsNode)) { + //If removed, return silently + if (isRemoved(prefsNode)) + return; + + // Import any preferences at this node + Element firstXmlKid = (Element) xmlKids.item(0); + ImportPrefs(prefsNode, firstXmlKid); + prefsKids = new Preferences[numXmlKids - 1]; + + // Get involved children + for (int i=1; i < numXmlKids; i++) { + Element xmlKid = (Element) xmlKids.item(i); + prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name")); + } + } // unlocked the node + // import children + for (int i=1; i < numXmlKids; i++) + ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i)); + } + + /** + * Import the preferences described by the specified XML element + * (a map from a preferences document) into the specified + * preferences node. + */ + private static void ImportPrefs(Preferences prefsNode, Element map) { + NodeList entries = map.getChildNodes(); + for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) { + Element entry = (Element) entries.item(i); + prefsNode.put(entry.getAttribute("key"), + entry.getAttribute("value")); + } + } + + /** + * Export the specified Map to a map document on + * the specified OutputStream as per the prefs DTD. This is used + * as the internal (undocumented) format for FileSystemPrefs. + * + * @throws IOException if writing to the specified output stream + * results in an IOException. + */ + public void exportMap(OutputStream os, Map map) throws IOException { + Document doc = createPrefsDoc("map"); + Element xmlMap = doc.getDocumentElement( ) ; + xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION); + + for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) { + Map.Entry e = (Map.Entry) i.next(); + Element xe = (Element) + xmlMap.appendChild(doc.createElement("entry")); + xe.setAttribute("key", (String) e.getKey()); + xe.setAttribute("value", (String) e.getValue()); + } + + writeDoc(doc, os); + } + + /** + * Import Map from the specified input stream, which is assumed + * to contain a map document as per the prefs DTD. This is used + * as the internal (undocumented) format for FileSystemPrefs. The + * key-value pairs specified in the XML document will be put into + * the specified Map. (If this Map is empty, it will contain exactly + * the key-value pairs int the XML-document when this method returns.) + * + * @throws IOException if reading from the specified output stream + * results in an IOException. + * @throws InvalidPreferencesFormatException Data on input stream does not + * constitute a valid XML document with the mandated document type. + */ + public void importMap(InputStream is, Map m) + throws IOException, InvalidPreferencesFormatException + { + try { + Document doc = loadPrefsDoc(is); + Element xmlMap = doc.getDocumentElement(); + // check version + String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION"); + if (mapVersion.compareTo(MAP_XML_VERSION) > 0) + throw new InvalidPreferencesFormatException( + "Preferences map file format version " + mapVersion + + " is not supported. This java installation can read" + + " versions " + MAP_XML_VERSION + " or older. You may need" + + " to install a newer version of JDK."); + + NodeList entries = xmlMap.getChildNodes(); + for (int i=0, numEntries=entries.getLength(); i" + + "" + + ""+ + "" + + "" + + "" + + ""; + + /** + * Version number for the format of exported properties files. + */ + private static final String EXTERNAL_XML_VERSION = "1.0"; + + void doLoad(Properties props, InputStream in) + throws IOException, InvalidPropertiesFormatException + { + Document doc = null; + try { + doc = getLoadingDoc(in); + } catch (SAXException saxe) { + throw new InvalidPropertiesFormatException(saxe); + } + Element propertiesElement = (Element)doc.getChildNodes().item(1); + String xmlVersion = propertiesElement.getAttribute("version"); + if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) + throw new InvalidPropertiesFormatException( + "Exported Properties file format version " + xmlVersion + + " is not supported. This java installation can read" + + " versions " + EXTERNAL_XML_VERSION + " or older. You" + + " may need to install a newer version of JDK."); + importProperties(props, propertiesElement); + } + + static Document getLoadingDoc(InputStream in) + throws SAXException, IOException + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setIgnoringElementContentWhitespace(true); + dbf.setValidating(true); + dbf.setCoalescing(true); + dbf.setIgnoringComments(true); + try { + DocumentBuilder db = dbf.newDocumentBuilder(); + db.setEntityResolver(new Resolver()); + db.setErrorHandler(new EH()); + InputSource is = new InputSource(in); + return db.parse(is); + } catch (ParserConfigurationException x) { + throw new Error(x); + } + } + + static void importProperties(Properties props, Element propertiesElement) { + NodeList entries = propertiesElement.getChildNodes(); + int numEntries = entries.getLength(); + int start = numEntries > 0 && + entries.item(0).getNodeName().equals("comment") ? 1 : 0; + for (int i=start; i" + - "" + - ""+ - "" + - "" + - "" + - ""; - - /** - * Version number for the format of exported properties files. - */ - private static final String EXTERNAL_XML_VERSION = "1.0"; - - static void load(Properties props, InputStream in) - throws IOException, InvalidPropertiesFormatException - { - Document doc = null; - try { - doc = getLoadingDoc(in); - } catch (SAXException saxe) { - throw new InvalidPropertiesFormatException(saxe); - } - Element propertiesElement = (Element)doc.getChildNodes().item(1); - String xmlVersion = propertiesElement.getAttribute("version"); - if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) - throw new InvalidPropertiesFormatException( - "Exported Properties file format version " + xmlVersion + - " is not supported. This java installation can read" + - " versions " + EXTERNAL_XML_VERSION + " or older. You" + - " may need to install a newer version of JDK."); - importProperties(props, propertiesElement); - } - - static Document getLoadingDoc(InputStream in) - throws SAXException, IOException - { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setIgnoringElementContentWhitespace(true); - dbf.setValidating(true); - dbf.setCoalescing(true); - dbf.setIgnoringComments(true); - try { - DocumentBuilder db = dbf.newDocumentBuilder(); - db.setEntityResolver(new Resolver()); - db.setErrorHandler(new EH()); - InputSource is = new InputSource(in); - return db.parse(is); - } catch (ParserConfigurationException x) { - throw new Error(x); - } - } - - static void importProperties(Properties props, Element propertiesElement) { - NodeList entries = propertiesElement.getChildNodes(); - int numEntries = entries.getLength(); - int start = numEntries > 0 && - entries.item(0).getNodeName().equals("comment") ? 1 : 0; - for (int i=start; i" + - - "" + - - "" + - "" + - - "" + - "" + - - "" + - "" + - - "" + - "" + - "" + - "" ; - /** - * Version number for the format exported preferences files. - */ - private static final String EXTERNAL_XML_VERSION = "1.0"; - - /* - * Version number for the internal map files. - */ - private static final String MAP_XML_VERSION = "1.0"; - - /** - * Export the specified preferences node and, if subTree is true, all - * subnodes, to the specified output stream. Preferences are exported as - * an XML document conforming to the definition in the Preferences spec. - * - * @throws IOException if writing to the specified output stream - * results in an IOException. - * @throws BackingStoreException if preference data cannot be read from - * backing store. - * @throws IllegalStateException if this node (or an ancestor) has been - * removed with the {@link #removeNode()} method. - */ - static void export(OutputStream os, final Preferences p, boolean subTree) - throws IOException, BackingStoreException { - if (((AbstractPreferences)p).isRemoved()) - throw new IllegalStateException("Node has been removed"); - Document doc = createPrefsDoc("preferences"); - Element preferences = doc.getDocumentElement() ; - preferences.setAttribute("EXTERNAL_XML_VERSION", EXTERNAL_XML_VERSION); - Element xmlRoot = (Element) - preferences.appendChild(doc.createElement("root")); - xmlRoot.setAttribute("type", (p.isUserNode() ? "user" : "system")); - - // Get bottom-up list of nodes from p to root, excluding root - List ancestors = new ArrayList(); - - for (Preferences kid = p, dad = kid.parent(); dad != null; - kid = dad, dad = kid.parent()) { - ancestors.add(kid); - } - Element e = xmlRoot; - for (int i=ancestors.size()-1; i >= 0; i--) { - e.appendChild(doc.createElement("map")); - e = (Element) e.appendChild(doc.createElement("node")); - e.setAttribute("name", ((Preferences)ancestors.get(i)).name()); - } - putPreferencesInXml(e, doc, p, subTree); - - writeDoc(doc, os); - } - - /** - * Put the preferences in the specified Preferences node into the - * specified XML element which is assumed to represent a node - * in the specified XML document which is assumed to conform to - * PREFS_DTD. If subTree is true, create children of the specified - * XML node conforming to all of the children of the specified - * Preferences node and recurse. - * - * @throws BackingStoreException if it is not possible to read - * the preferences or children out of the specified - * preferences node. - */ - private static void putPreferencesInXml(Element elt, Document doc, - Preferences prefs, boolean subTree) throws BackingStoreException - { - Preferences[] kidsCopy = null; - String[] kidNames = null; - - // Node is locked to export its contents and get a - // copy of children, then lock is released, - // and, if subTree = true, recursive calls are made on children - synchronized (((AbstractPreferences)prefs).lock) { - // Check if this node was concurrently removed. If yes - // remove it from XML Document and return. - if (((AbstractPreferences)prefs).isRemoved()) { - elt.getParentNode().removeChild(elt); - return; - } - // Put map in xml element - String[] keys = prefs.keys(); - Element map = (Element) elt.appendChild(doc.createElement("map")); - for (int i=0; iIOException. - * @throws InvalidPreferencesFormatException Data on input stream does not - * constitute a valid XML document with the mandated document type. - */ - static void importPreferences(InputStream is) - throws IOException, InvalidPreferencesFormatException - { - try { - Document doc = loadPrefsDoc(is); - String xmlVersion = - doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION"); - if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) - throw new InvalidPreferencesFormatException( - "Exported preferences file format version " + xmlVersion + - " is not supported. This java installation can read" + - " versions " + EXTERNAL_XML_VERSION + " or older. You may need" + - " to install a newer version of JDK."); - - Element xmlRoot = (Element) doc.getDocumentElement(). - getChildNodes().item(0); - Preferences prefsRoot = - (xmlRoot.getAttribute("type").equals("user") ? - Preferences.userRoot() : Preferences.systemRoot()); - ImportSubtree(prefsRoot, xmlRoot); - } catch(SAXException e) { - throw new InvalidPreferencesFormatException(e); - } - } - - /** - * Create a new prefs XML document. - */ - private static Document createPrefsDoc( String qname ) { - try { - DOMImplementation di = DocumentBuilderFactory.newInstance(). - newDocumentBuilder().getDOMImplementation(); - DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI); - return di.createDocument(null, qname, dt); - } catch(ParserConfigurationException e) { - throw new AssertionError(e); - } - } - - /** - * Load an XML document from specified input stream, which must - * have the requisite DTD URI. - */ - private static Document loadPrefsDoc(InputStream in) - throws SAXException, IOException - { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setIgnoringElementContentWhitespace(true); - dbf.setValidating(true); - dbf.setCoalescing(true); - dbf.setIgnoringComments(true); - try { - DocumentBuilder db = dbf.newDocumentBuilder(); - db.setEntityResolver(new Resolver()); - db.setErrorHandler(new EH()); - return db.parse(new InputSource(in)); - } catch (ParserConfigurationException e) { - throw new AssertionError(e); - } - } - - /** - * Write XML document to the specified output stream. - */ - private static final void writeDoc(Document doc, OutputStream out) - throws IOException - { - try { - TransformerFactory tf = TransformerFactory.newInstance(); - try { - tf.setAttribute("indent-number", new Integer(2)); - } catch (IllegalArgumentException iae) { - //Ignore the IAE. Should not fail the writeout even the - //transformer provider does not support "indent-number". - } - Transformer t = tf.newTransformer(); - t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); - t.setOutputProperty(OutputKeys.INDENT, "yes"); - //Transformer resets the "indent" info if the "result" is a StreamResult with - //an OutputStream object embedded, creating a Writer object on top of that - //OutputStream object however works. - t.transform(new DOMSource(doc), - new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")))); - } catch(TransformerException e) { - throw new AssertionError(e); - } - } - - /** - * Recursively traverse the specified preferences node and store - * the described preferences into the system or current user - * preferences tree, as appropriate. - */ - private static void ImportSubtree(Preferences prefsNode, Element xmlNode) { - NodeList xmlKids = xmlNode.getChildNodes(); - int numXmlKids = xmlKids.getLength(); - /* - * We first lock the node, import its contents and get - * child nodes. Then we unlock the node and go to children - * Since some of the children might have been concurrently - * deleted we check for this. - */ - Preferences[] prefsKids; - /* Lock the node */ - synchronized (((AbstractPreferences)prefsNode).lock) { - //If removed, return silently - if (((AbstractPreferences)prefsNode).isRemoved()) - return; - - // Import any preferences at this node - Element firstXmlKid = (Element) xmlKids.item(0); - ImportPrefs(prefsNode, firstXmlKid); - prefsKids = new Preferences[numXmlKids - 1]; - - // Get involved children - for (int i=1; i < numXmlKids; i++) { - Element xmlKid = (Element) xmlKids.item(i); - prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name")); - } - } // unlocked the node - // import children - for (int i=1; i < numXmlKids; i++) - ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i)); - } - - /** - * Import the preferences described by the specified XML element - * (a map from a preferences document) into the specified - * preferences node. - */ - private static void ImportPrefs(Preferences prefsNode, Element map) { - NodeList entries = map.getChildNodes(); - for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) { - Element entry = (Element) entries.item(i); - prefsNode.put(entry.getAttribute("key"), - entry.getAttribute("value")); - } - } - - /** - * Export the specified Map to a map document on - * the specified OutputStream as per the prefs DTD. This is used - * as the internal (undocumented) format for FileSystemPrefs. - * - * @throws IOException if writing to the specified output stream - * results in an IOException. - */ - static void exportMap(OutputStream os, Map map) throws IOException { - Document doc = createPrefsDoc("map"); - Element xmlMap = doc.getDocumentElement( ) ; - xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION); - - for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) { - Map.Entry e = (Map.Entry) i.next(); - Element xe = (Element) - xmlMap.appendChild(doc.createElement("entry")); - xe.setAttribute("key", (String) e.getKey()); - xe.setAttribute("value", (String) e.getValue()); - } - - writeDoc(doc, os); - } - - /** - * Import Map from the specified input stream, which is assumed - * to contain a map document as per the prefs DTD. This is used - * as the internal (undocumented) format for FileSystemPrefs. The - * key-value pairs specified in the XML document will be put into - * the specified Map. (If this Map is empty, it will contain exactly - * the key-value pairs int the XML-document when this method returns.) - * - * @throws IOException if reading from the specified output stream - * results in an IOException. - * @throws InvalidPreferencesFormatException Data on input stream does not - * constitute a valid XML document with the mandated document type. - */ - static void importMap(InputStream is, Map m) - throws IOException, InvalidPreferencesFormatException - { - try { - Document doc = loadPrefsDoc(is); - Element xmlMap = doc.getDocumentElement(); - // check version - String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION"); - if (mapVersion.compareTo(MAP_XML_VERSION) > 0) - throw new InvalidPreferencesFormatException( - "Preferences map file format version " + mapVersion + - " is not supported. This java installation can read" + - " versions " + MAP_XML_VERSION + " or older. You may need" + - " to install a newer version of JDK."); - - NodeList entries = xmlMap.getChildNodes(); - for (int i=0, numEntries=entries.getLength(); i"); + w.println(""); + w.println(""); + w.print(" "); + LinkedList ancestors = new LinkedList(); + for (Preferences kid = p, dad = kid.parent(); dad != null; + kid = dad, dad = kid.parent()) { + ancestors.addFirst(kid); + } + String indent = " "; + for (Preferences pref : ancestors) { + indent = " " + indent; + w.print(indent); w.println(""); + w.print(indent); w.print(""); + } + + putPreferencesInXml(w, indent + " ", p, subTree); + for (Preferences pref : ancestors) { + w.print(indent); w.println(""); + indent = indent.substring(2); + } + + w.println(" "); + w.println(""); + w.flush(); + } + + private static void putPreferencesInXml( + PrintWriter w, String indent, Preferences prefs, boolean subTree + ) throws BackingStoreException { + Preferences[] kidsCopy = null; + String[] kidNames = null; + + // Node is locked to export its contents and get a + // copy of children, then lock is released, + // and, if subTree = true, recursive calls are made on children + synchronized (lock(prefs)) { + // Check if this node was concurrently removed. If yes + // don't print it + if (isRemoved(prefs)) { + return; + } + // Put map in xml element + String[] keys = prefs.keys(); + if (keys.length == 0) { + w.print(indent); w.println(""); + } else { + w.print(indent); w.println(""); + for (int i=0; i"); + } + w.print(indent); w.println(""); + } + // Recurse if appropriate + if (subTree) { + /* Get a copy of kids while lock is held */ + kidNames = prefs.childrenNames(); + kidsCopy = new Preferences[kidNames.length]; + for (int i = 0; i < kidNames.length; i++) + kidsCopy[i] = prefs.node(kidNames[i]); + } + // release lock + } + + if (subTree) { + for (int i=0; i < kidNames.length; i++) { + w.print(indent); w.print(""); + putPreferencesInXml(w, " " + indent, kidsCopy[i], subTree); + w.print(indent); w.println(""); + } + } + } + + /** + * Import preferences from the specified input stream, which is assumed + * to contain an XML document in the format described in the Preferences + * spec. + * + * @throws IOException if reading from the specified output stream + * results in an IOException. + * @throws InvalidPreferencesFormatException Data on input stream does not + * constitute a valid XML document with the mandated document type. + */ + public void importPreferences(InputStream is) + throws IOException, InvalidPreferencesFormatException + { + try { + XMLElement doc = new XMLElement(); + doc.parseFromReader(new InputStreamReader(is, "UTF-8")); + String xmlVersion = (String)doc.getAttribute("EXTERNAL_XML_VERSION"); + if (xmlVersion.compareTo("1.0") > 0) + throw new InvalidPreferencesFormatException( + "Exported preferences file format version " + xmlVersion + + " is not supported. This java installation can read" + + " versions " + "1.0" + " or older. You may need" + + " to install a newer version of JDK."); + + XMLElement xmlRoot = (XMLElement) doc.getChildren().get(0); + Preferences prefsRoot = + (xmlRoot.getAttribute("type").equals("user") ? + Preferences.userRoot() : Preferences.systemRoot()); + ImportSubtree(prefsRoot, xmlRoot); + } catch(XMLParseException e) { + throw new InvalidPreferencesFormatException(e); + } + } + + /** + * Create a new prefs XML document. + * + private static Document createPrefsDoc( String qname ) { + try { + DOMImplementation di = DocumentBuilderFactory.newInstance(). + newDocumentBuilder().getDOMImplementation(); + DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI); + return di.createDocument(null, qname, dt); + } catch(ParserConfigurationException e) { + throw new AssertionError(e); + } + } + + + /** + * Write XML document to the specified output stream. + * + private static final void writeDoc(Document doc, OutputStream out) + throws IOException + { + try { + TransformerFactory tf = TransformerFactory.newInstance(); + try { + tf.setAttribute("indent-number", new Integer(2)); + } catch (IllegalArgumentException iae) { + //Ignore the IAE. Should not fail the writeout even the + //transformer provider does not support "indent-number". + } + Transformer t = tf.newTransformer(); + t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); + t.setOutputProperty(OutputKeys.INDENT, "yes"); + //Transformer resets the "indent" info if the "result" is a StreamResult with + //an OutputStream object embedded, creating a Writer object on top of that + //OutputStream object however works. + t.transform(new DOMSource(doc), + new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")))); + } catch(TransformerException e) { + throw new AssertionError(e); + } + } + + /** + * Recursively traverse the specified preferences node and store + * the described preferences into the system or current user + * preferences tree, as appropriate. + */ + private static void ImportSubtree(Preferences prefsNode, XMLElement xmlNode) { + Vector xmlKids = xmlNode.getChildren(); + int numXmlKids = xmlKids.size(); + /* + * We first lock the node, import its contents and get + * child nodes. Then we unlock the node and go to children + * Since some of the children might have been concurrently + * deleted we check for this. + */ + Preferences[] prefsKids; + /* Lock the node */ + synchronized (lock(prefsNode)) { + //If removed, return silently + if (isRemoved(prefsNode)) + return; + + // Import any preferences at this node + XMLElement firstXmlKid = (XMLElement) xmlKids.get(0); + ImportPrefs(prefsNode, firstXmlKid); + prefsKids = new Preferences[numXmlKids - 1]; + + // Get involved children + for (int i=1; i < numXmlKids; i++) { + XMLElement xmlKid = (XMLElement) xmlKids.get(i); + prefsKids[i-1] = prefsNode.node((String)xmlKid.getAttribute("name")); + } + } // unlocked the node + // import children + for (int i=1; i < numXmlKids; i++) + ImportSubtree(prefsKids[i-1], (XMLElement)xmlKids.get(i)); + } + + /** + * Import the preferences described by the specified XML element + * (a map from a preferences document) into the specified + * preferences node. + */ + private static void ImportPrefs(Preferences prefsNode, XMLElement map) { + Vector entries = map.getChildren(); + for (int i=0, numEntries = entries.size(); i < numEntries; i++) { + XMLElement entry = (XMLElement) entries.get(i); + prefsNode.put((String)entry.getAttribute("key"), + (String)entry.getAttribute("value")); + } + } + + /** + * Export the specified Map to a map document on + * the specified OutputStream as per the prefs DTD. This is used + * as the internal (undocumented) format for FileSystemPrefs. + * + * @throws IOException if writing to the specified output stream + * results in an IOException. + */ + public void exportMap(OutputStream os, Map map) throws IOException { + /* + Document doc = createPrefsDoc("map"); + Element xmlMap = doc.getDocumentElement( ) ; + xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION); + + for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) { + Map.Entry e = (Map.Entry) i.next(); + Element xe = (Element) + xmlMap.appendChild(doc.createElement("entry")); + xe.setAttribute("key", (String) e.getKey()); + xe.setAttribute("value", (String) e.getValue()); + } + + writeDoc(doc, os); + */ + } + + /** + * Import Map from the specified input stream, which is assumed + * to contain a map document as per the prefs DTD. This is used + * as the internal (undocumented) format for FileSystemPrefs. The + * key-value pairs specified in the XML document will be put into + * the specified Map. (If this Map is empty, it will contain exactly + * the key-value pairs int the XML-document when this method returns.) + * + * @throws IOException if reading from the specified output stream + * results in an IOException. + * @throws InvalidPreferencesFormatException Data on input stream does not + * constitute a valid XML document with the mandated document type. + */ + public void importMap(InputStream is, Map m) + throws IOException, InvalidPreferencesFormatException + { + /* + try { + Document doc = loadPrefsDoc(is); + Element xmlMap = doc.getDocumentElement(); + // check version + String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION"); + if (mapVersion.compareTo(MAP_XML_VERSION) > 0) + throw new InvalidPreferencesFormatException( + "Preferences map file format version " + mapVersion + + " is not supported. This java installation can read" + + " versions " + MAP_XML_VERSION + " or older. You may need" + + " to install a newer version of JDK."); + + NodeList entries = xmlMap.getChildNodes(); + for (int i=0, numEntries=entries.getLength(); i"); + w.println(""); + w.println(""); + if (comment != null) { + w.print(""); + w.print(comment); + w.println(""); + } + for (Entry entry : props.entrySet()) { + w.print(""); + w.print(entry.getValue()); + w.println(""); + } + w.println(""); + w.flush(); + } + +} diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/sun/util/xml/PrefsXmlSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/util/xml/PrefsXmlSupport.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.util.xml; + +import java.util.*; +import java.util.prefs.*; +import java.io.*; + +/** + * XML Support for java.util.prefs. Methods to import and export preference + * nodes and subtrees. + * + * @author Josh Bloch and Mark Reinhold + * @see Preferences + * @since 1.4 + */ +public abstract class PrefsXmlSupport { + public static PrefsXmlSupport getDefault() { + Iterator it = ServiceLoader.load(PrefsXmlSupport.class).iterator(); + return it.hasNext() ? it.next() : new DefaultPrefsXmlSupport(); + } + + public abstract void export(OutputStream os, final Preferences p, boolean subTree) + throws IOException, BackingStoreException; + + /** + * Import preferences from the specified input stream, which is assumed + * to contain an XML document in the format described in the Preferences + * spec. + * + * @throws IOException if reading from the specified output stream + * results in an IOException. + * @throws InvalidPreferencesFormatException Data on input stream does not + * constitute a valid XML document with the mandated document type. + */ + public abstract void importPreferences(InputStream is) + throws IOException, InvalidPreferencesFormatException; + + + public abstract void importMap(InputStream is, Map m) + throws IOException, InvalidPreferencesFormatException; + + public abstract void exportMap(OutputStream is, Map m) + throws IOException, InvalidPreferencesFormatException; + +} diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/sun/util/xml/PropertiesXMLUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/util/xml/PropertiesXMLUtils.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.util.xml; + +import java.io.*; +import java.util.Properties; +import java.util.InvalidPropertiesFormatException; +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * A class used to aid in Properties load and save in XML. Keeping this + * code outside of Properties helps reduce the number of classes loaded + * when Properties is loaded. + * + * @author Michael McCloskey + * @since 1.3 + */ +public abstract class PropertiesXMLUtils { + public static PropertiesXMLUtils getDefault() { + Iterator it = ServiceLoader.load(PropertiesXMLUtils.class).iterator(); + return it.hasNext() ? it.next() : new DefaultPropertiesXMLUtils(); + } + + public abstract void load(Properties props, InputStream in) throws + IOException, InvalidPropertiesFormatException; + public abstract void save(Properties props, OutputStream out, String comment, String encoding) + throws IOException; + +} diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/sun/util/xml/XMLElement.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/util/xml/XMLElement.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,2874 @@ +/* XMLElement.java + * + * $Revision: 1.4 $ + * $Date: 2002/03/24 10:27:59 $ + * $Name: RELEASE_2_2_1 $ + * + * This file is part of NanoXML 2 Lite. + * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + *****************************************************************************/ + + +package sun.util.xml; + + +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.Vector; + + +/** + * XMLElement is a representation of an XML object. The object is able to parse + * XML code. + *

+ *
Parsing XML Data
+ *
+ * You can parse XML data using the following code: + *
    + * XMLElement xml = new XMLElement();
    + * FileReader reader = new FileReader("filename.xml");
    + * xml.parseFromReader(reader); + *
+ *
Retrieving Attributes
+ *
+ * You can enumerate the attributes of an element using the method + * {@link #enumerateAttributeNames() enumerateAttributeNames}. + * The attribute values can be retrieved using the method + * {@link #getStringAttribute(java.lang.String) getStringAttribute}. + * The following example shows how to list the attributes of an element: + *
    + * XMLElement element = ...;
    + * Enumeration enumV = element.getAttributeNames();
    + * while (enumV.hasMoreElements()) {
    + *     String key = (String) enumV.nextElement();
    + *     String value = element.getStringAttribute(key);
    + *     System.out.println(key + " = " + value);
    + * } + *
+ *
Retrieving Child Elements
+ *
+ * You can enumerate the children of an element using + * {@link #enumerateChildren() enumerateChildren}. + * The number of child elements can be retrieved using + * {@link #countChildren() countChildren}. + *
+ *
Elements Containing Character Data
+ *
+ * If an elements contains character data, like in the following example: + *
    + * <title>The Title</title> + *
+ * you can retrieve that data using the method + * {@link #getContent() getContent}. + *
+ *
Subclassing XMLElement
+ *
+ * When subclassing XMLElement, you need to override the method + * {@link #createAnotherElement() createAnotherElement} + * which has to return a new copy of the receiver. + *
+ *

+ * + * @see nanoxml.XMLParseException + * + * @author Marc De Scheemaecker + * <cyberelf@mac.com> + * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $ + */ +class XMLElement { + + /** + * Serialization serial version ID. + */ + static final long serialVersionUID = 6685035139346394777L; + + + /** + * Major version of NanoXML. Classes with the same major and minor + * version are binary compatible. Classes with the same major version + * are source compatible. If the major version is different, you may + * need to modify the client source code. + * + * @see nanoxml.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 nanoxml.XMLElement#NANOXML_MAJOR_VERSION + */ + public static final int NANOXML_MINOR_VERSION = 2; + + + /** + * The attributes given to the element. + * + *

Invariants:
+ *
  • The field can be empty. + *
  • The field is never null. + *
  • The keys and the values are strings. + *
+ */ + private Hashtable attributes; + + + /** + * Child elements of the element. + * + *
Invariants:
+ *
  • The field can be empty. + *
  • The field is never null. + *
  • The elements are instances of XMLElement + * or a subclass of XMLElement. + *
+ */ + private Vector children; + + + /** + * The name of the element. + * + *
Invariants:
+ *
  • The field is null iff the element is not + * initialized by either parse or setName. + *
  • If the field is not null, it's not empty. + *
  • If the field is not null, it contains a valid + * XML identifier. + *
+ */ + private String name; + + + /** + * The #PCDATA content of the object. + * + *
Invariants:
+ *
  • The field is null iff the element is not a + * #PCDATA element. + *
  • The field can be any string, including the empty string. + *
+ */ + private String contents; + + + /** + * Conversion table for &...; entities. The keys are the entity names + * without the & and ; delimiters. + * + *
Invariants:
+ *
  • The field is never null. + *
  • The field always contains the following associations: + * "lt" => "<", "gt" => ">", + * "quot" => "\"", "apos" => "'", + * "amp" => "&" + *
  • The keys are strings + *
  • The values are char arrays + *
+ */ + private Hashtable entities; + + + /** + * The line number where the element starts. + * + *
Invariants:
+ *
  • lineNr >= 0 + *
+ */ + private int lineNr; + + + /** + * true if the case of the element and attribute names + * are case insensitive. + */ + private boolean ignoreCase; + + + /** + * true if the leading and trailing whitespace of #PCDATA + * sections have to be ignored. + */ + private boolean ignoreWhitespace; + + + /** + * Character read too much. + * This character provides push-back functionality to the input reader + * without having to use a PushbackReader. + * If there is no such character, this field is '\0'. + */ + private char charReadTooMuch; + + + /** + * The reader provided by the caller of the parse method. + * + *
Invariants:
+ *
  • The field is not null while the parse method + * is running. + *
+ */ + private Reader reader; + + + /** + * The current line number in the source content. + * + *
Invariants:
+ *
  • parserLineNr > 0 while the parse method is running. + *
+ */ + private int parserLineNr; + + + /** + * Creates and initializes a new XML element. + * Calling the construction is equivalent to: + *
    new XMLElement(new Hashtable(), false, true) + *
+ * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable) + * XMLElement(Hashtable) + * @see nanoxml.XMLElement#XMLElement(boolean) + * @see nanoxml.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: + *
    new XMLElement(entities, false, true) + *
+ * + * @param entities + * The entity conversion table. + * + *
Preconditions:
+ *
  • entities != null + *
+ * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.XMLElement#XMLElement() + * @see nanoxml.XMLElement#XMLElement(boolean) + * @see nanoxml.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: + *
    new XMLElement(new Hashtable(), skipLeadingWhitespace, true) + *
+ * + * @param skipLeadingWhitespace + * true if leading and trailing whitespace in PCDATA + * content has to be removed. + * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.XMLElement#XMLElement() + * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable) + * XMLElement(Hashtable) + * @see nanoxml.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: + *
    new XMLElement(entities, skipLeadingWhitespace, true) + *
+ * + * @param entities + * The entity conversion table. + * @param skipLeadingWhitespace + * true if leading and trailing whitespace in PCDATA + * content has to be removed. + * + *
Preconditions:
+ *
  • entities != null + *
+ * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.XMLElement#XMLElement() + * @see nanoxml.XMLElement#XMLElement(boolean) + * @see nanoxml.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. + * + * @param entities + * The entity conversion table. + * @param skipLeadingWhitespace + * true if leading and trailing whitespace in PCDATA + * content has to be removed. + * @param ignoreCase + * true if the case of element and attribute names have + * to be ignored. + * + *
Preconditions:
+ *
  • entities != null + *
+ * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.XMLElement#XMLElement() + * @see nanoxml.XMLElement#XMLElement(boolean) + * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable) + * XMLElement(Hashtable) + * @see nanoxml.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. + *

+ * This constructor should only be called from + * {@link #createAnotherElement() createAnotherElement} + * to create child elements. + * + * @param entities + * The entity conversion table. + * @param skipLeadingWhitespace + * true if leading and trailing whitespace in PCDATA + * content has to be removed. + * @param fillBasicConversionTable + * true if the basic entities need to be added to + * the entity list. + * @param ignoreCase + * true if the case of element and attribute names have + * to be ignored. + * + *

Preconditions:
+ *
  • entities != null + *
  • if fillBasicConversionTable == false + * then entities contains at least the following + * entries: amp, lt, gt, + * apos and quot + *
+ * + *
Postconditions:
+ *
  • countChildren() => 0 + *
  • enumerateChildren() => empty enumeration + *
  • enumeratePropertyNames() => empty enumeration + *
  • getChildren() => empty vector + *
  • getContent() => "" + *
  • getLineNr() => 0 + *
  • getName() => null + *
+ * + * @see nanoxml.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 Hashtable(); + this.children = new Vector(); + this.entities = entities; + this.lineNr = 0; + Enumeration enumV = this.entities.keys(); + while (enumV.hasMoreElements()) { + Object key = enumV.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[] { '>' }); + } + } + + + /** + * Adds a child element. + * + * @param child + * The child element to add. + * + *
Preconditions:
+ *
  • child != null + *
  • child.getName() != null + *
  • child does not have a parent element + *
+ * + *
Postconditions:
+ *
  • countChildren() => old.countChildren() + 1 + *
  • enumerateChildren() => old.enumerateChildren() + child + *
  • getChildren() => old.enumerateChildren() + child + *
+ * + * @see nanoxml.XMLElement#countChildren() + * @see nanoxml.XMLElement#enumerateChildren() + * @see nanoxml.XMLElement#getChildren() + * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement) + * removeChild(XMLElement) + */ + public void addChild(XMLElement child) + { + this.children.addElement(child); + } + + + /** + * Adds or modifies an attribute. + * + * @param name + * The name of the attribute. + * @param value + * The value of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • value != null + *
+ * + *
Postconditions:
+ *
  • enumerateAttributeNames() + * => old.enumerateAttributeNames() + name + *
  • getAttribute(name) => value + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getAttribute(java.lang.String) + * getAttribute(String) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object) + * getAttribute(String, Object) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String) + * getStringAttribute(String) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.lang.String) + * getStringAttribute(String, String) + * @see nanoxml.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(); + } + this.attributes.put(name, value.toString()); + } + + + /** + * Adds or modifies an attribute. + * + * @param name + * The name of the attribute. + * @param value + * The value of the attribute. + * + * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object) + * setAttribute} instead. + */ + public void addProperty(String name, + Object value) + { + this.setAttribute(name, value); + } + + + /** + * Adds or modifies an attribute. + * + * @param name + * The name of the attribute. + * @param value + * The value of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + *
Postconditions:
+ *
  • enumerateAttributeNames() + * => old.enumerateAttributeNames() + name + *
  • getIntAttribute(name) => value + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String) + * getIntAttribute(String) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int) + * getIntAttribute(String, int) + * @see nanoxml.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(); + } + this.attributes.put(name, Integer.toString(value)); + } + + + /** + * Adds or modifies an attribute. + * + * @param name + * The name of the attribute. + * @param value + * 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. + * + * @param name + * The name of the attribute. + * @param value + * The value of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + *
Postconditions:
+ *
  • enumerateAttributeNames() + * => old.enumerateAttributeNames() + name + *
  • getDoubleAttribute(name) => value + *
+ * + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String) + * getDoubleAttribute(String) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double) + * getDoubleAttribute(String, double) + * @see nanoxml.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(); + } + this.attributes.put(name, Double.toString(value)); + } + + + /** + * Adds or modifies an attribute. + * + * @param name + * The name of the attribute. + * @param value + * The value of the attribute. + * + * @deprecated Use {@link #setDoubleAttribute(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. + * + *
Postconditions:
+ *
  • result >= 0 + *
+ * + * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) + * addChild(XMLElement) + * @see nanoxml.XMLElement#enumerateChildren() + * @see nanoxml.XMLElement#getChildren() + * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement) + * removeChild(XMLElement) + */ + public int countChildren() + { + return this.children.size(); + } + + + /** + * Enumerates the attribute names. + * + *
Postconditions:
+ *
  • result != null + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#getAttribute(java.lang.String) + * getAttribute(String) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object) + * getAttribute(String, String) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String) + * getStringAttribute(String) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.lang.String) + * getStringAttribute(String, String) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getStringAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String) + * getIntAttribute(String) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int) + * getIntAttribute(String, int) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getIntAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String) + * getDoubleAttribute(String) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double) + * getDoubleAttribute(String, double) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getDoubleAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String, + * java.lang.String, + * java.lang.String, boolean) + * getBooleanAttribute(String, String, String, boolean) + */ + public Enumeration enumerateAttributeNames() + { + return this.attributes.keys(); + } + + + /** + * Enumerates the attribute names. + * + * @deprecated Use {@link #enumerateAttributeNames() + * enumerateAttributeNames} instead. + */ + public Enumeration enumeratePropertyNames() + { + return this.enumerateAttributeNames(); + } + + + /** + * Enumerates the child elements. + * + *
Postconditions:
+ *
  • result != null + *
+ * + * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) + * addChild(XMLElement) + * @see nanoxml.XMLElement#countChildren() + * @see nanoxml.XMLElement#getChildren() + * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement) + * removeChild(XMLElement) + */ + public Enumeration enumerateChildren() + { + return this.children.elements(); + } + + + /** + * Returns the child elements as a Vector. It is safe to modify this + * Vector. + * + *
Postconditions:
+ *
  • result != null + *
+ * + * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) + * addChild(XMLElement) + * @see nanoxml.XMLElement#countChildren() + * @see nanoxml.XMLElement#enumerateChildren() + * @see nanoxml.XMLElement#removeChild(nanoxml.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, + * null is returned. + * + * @deprecated Use {@link #getContent() getContent} instead. + */ + public String getContents() + { + return this.getContent(); + } + + + /** + * Returns the PCDATA content of the object. If there is no such content, + * null is returned. + * + * @see nanoxml.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 0 there is no associated source data. + * + *
Postconditions:
+ *
  • result >= 0 + *
+ */ + public int getLineNr() + { + return this.lineNr; + } + + + /** + * Returns an attribute of the element. + * If the attribute doesn't exist, null is returned. + * + * @param name The name of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object) + * getAttribute(String, Object) + * @see nanoxml.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, defaultValue is returned. + * + * @param name The name of the attribute. + * @param defaultValue Key to use if the attribute is missing. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getAttribute(java.lang.String) + * getAttribute(String) + * @see nanoxml.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(); + } + Object value = this.attributes.get(name); + if (value == null) { + value = defaultValue; + } + return value; + } + + + /** + * Returns an attribute by looking up a key in a hashtable. + * If the attribute doesn't exist, the value corresponding to defaultKey + * is returned. + *

+ * As an example, if valueSet contains the mapping "one" => + * "1" + * and the element contains the attribute attr="one", then + * getAttribute("attr", mapping, defaultKey, false) returns + * "1". + * + * @param name + * The name of the attribute. + * @param valueSet + * Hashtable mapping keys to values. + * @param defaultKey + * Key to use if the attribute is missing. + * @param allowLiterals + * true if literals are valid. + * + *

Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • valueSet != null + *
  • the keys of valueSet are strings + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getAttribute(java.lang.String) + * getAttribute(String) + * @see nanoxml.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(); + } + Object key = this.attributes.get(name); + Object result; + if (key == null) { + key = defaultKey; + } + result = valueSet.get(key); + if (result == null) { + if (allowLiterals) { + result = key; + } else { + throw this.invalidValue(name, (String) key); + } + } + return result; + } + + + /** + * Returns an attribute of the element. + * If the attribute doesn't exist, null is returned. + * + * @param name The name of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.lang.String) + * getStringAttribute(String, String) + * @see nanoxml.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, defaultValue is returned. + * + * @param name The name of the attribute. + * @param defaultValue Key to use if the attribute is missing. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String) + * getStringAttribute(String) + * @see nanoxml.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. + *

+ * As an example, if valueSet contains the mapping "one" => + * "1" + * and the element contains the attribute attr="one", then + * getAttribute("attr", mapping, defaultKey, false) returns + * "1". + * + * @param name + * The name of the attribute. + * @param valueSet + * Hashtable mapping keys to values. + * @param defaultKey + * Key to use if the attribute is missing. + * @param allowLiterals + * true if literals are valid. + * + *

Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • valueSet != null + *
  • the keys of valueSet are strings + *
  • the values of valueSet are strings + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String) + * getStringAttribute(String) + * @see nanoxml.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, 0 is returned. + * + * @param name The name of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int) + * getIntAttribute(String, int) + * @see nanoxml.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, defaultValue is returned. + * + * @param name The name of the attribute. + * @param defaultValue Key to use if the attribute is missing. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String) + * getIntAttribute(String) + * @see nanoxml.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(); + } + String value = (String) this.attributes.get(name); + if (value == null) { + return defaultValue; + } else { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw this.invalidValue(name, value); + } + } + } + + + /** + * Returns an attribute by looking up a key in a hashtable. + * If the attribute doesn't exist, the value corresponding to defaultKey + * is returned. + *

+ * As an example, if valueSet contains the mapping "one" => 1 + * and the element contains the attribute attr="one", then + * getIntAttribute("attr", mapping, defaultKey, false) returns + * 1. + * + * @param name + * The name of the attribute. + * @param valueSet + * Hashtable mapping keys to values. + * @param defaultKey + * Key to use if the attribute is missing. + * @param allowLiteralNumbers + * true if literal numbers are valid. + * + *

Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • valueSet != null + *
  • the keys of valueSet are strings + *
  • the values of valueSet are Integer objects + *
  • defaultKey is either null, a + * key in valueSet or an integer. + *
+ * + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String) + * getIntAttribute(String) + * @see nanoxml.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(); + } + 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, 0.0 is returned. + * + * @param name The name of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double) + * getDoubleAttribute(String, double) + * @see nanoxml.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, defaultValue is returned. + * + * @param name The name of the attribute. + * @param defaultValue Key to use if the attribute is missing. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String) + * getDoubleAttribute(String) + * @see nanoxml.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(); + } + 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. + *

+ * As an example, if valueSet contains the mapping "one" => + * 1.0 + * and the element contains the attribute attr="one", then + * getDoubleAttribute("attr", mapping, defaultKey, false) + * returns 1.0. + * + * @param name + * The name of the attribute. + * @param valueSet + * Hashtable mapping keys to values. + * @param defaultKey + * Key to use if the attribute is missing. + * @param allowLiteralNumbers + * true if literal numbers are valid. + * + *

Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • valueSet != null + *
  • the keys of valueSet are strings + *
  • the values of valueSet are Double objects + *
  • defaultKey is either null, a + * key in valueSet or a double. + *
+ * + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String) + * getDoubleAttribute(String) + * @see nanoxml.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(); + } + 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, defaultValue is returned. + * If the value of the attribute is equal to trueValue, + * true is returned. + * If the value of the attribute is equal to falseValue, + * false is returned. + * If the value doesn't match trueValue or + * falseValue, an exception is thrown. + * + * @param name The name of the attribute. + * @param trueValue The value associated with true. + * @param falseValue The value associated with true. + * @param defaultValue Value to use if the attribute is missing. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
  • trueValue and falseValue + * are different strings. + *
+ * + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#removeAttribute(java.lang.String) + * removeAttribute(String) + * @see nanoxml.XMLElement#enumerateAttributeNames() + */ + public boolean getBooleanAttribute(String name, + String trueValue, + String falseValue, + boolean defaultValue) + { + if (this.ignoreCase) { + name = name.toUpperCase(); + } + Object value = this.attributes.get(name); + if (value == null) { + return defaultValue; + } else if (value.equals(trueValue)) { + return true; + } else if (value.equals(falseValue)) { + return false; + } else { + throw this.invalidValue(name, (String) value); + } + } + + + /** + * Returns an attribute by looking up a key in a hashtable. + * + * @deprecated Use {@link #getIntAttribute(java.lang.String, + * java.util.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 nanoxml.XMLElement#setName(java.lang.String) setName(String) + */ + public String getName() + { + return this.name; + } + + + /** + * Returns the name of the element. + * + * @deprecated Use {@link #getName() getName} instead. + */ + public String getTagName() + { + return this.getName(); + } + + + /** + * Reads one XML element from a java.io.Reader and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * + *
Preconditions:
+ *
  • reader != null + *
  • reader is not closed + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
  • the reader points to the first character following the last + * '>' character of the XML element + *
+ * + * @throws java.io.IOException + * If an error occured while reading the input. + * @throws nanoxml.XMLParseException + * If an error occured while parsing the read data. + */ + public void parseFromReader(Reader reader) + throws IOException, XMLParseException + { + this.parseFromReader(reader, /*startingLineNr*/ 1); + } + + + /** + * Reads one XML element from a java.io.Reader and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param startingLineNr + * The line number of the first line in the data. + * + *
Preconditions:
+ *
  • reader != null + *
  • reader is not closed + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
  • the reader points to the first character following the last + * '>' character of the XML element + *
+ * + * @throws java.io.IOException + * If an error occured while reading the input. + * @throws nanoxml.XMLParseException + * If an error occured while parsing the read data. + */ + public void parseFromReader(Reader reader, + int startingLineNr) + throws IOException, XMLParseException + { + this.name = null; + this.contents = ""; + this.attributes = new Hashtable(); + this.children = new Vector(); + this.charReadTooMuch = '\0'; + this.reader = reader; + this.parserLineNr = startingLineNr; + + for (;;) { + char ch = this.scanWhitespace(); + + if (ch != '<') { + throw this.expectedInput("<"); + } + + ch = this.readChar(); + + if ((ch == '!') || (ch == '?')) { + this.skipSpecialTag(0); + } else { + this.unreadChar(ch); + this.scanElement(this); + return; + } + } + } + + + /** + * Reads one XML element from a String and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * + *
Preconditions:
+ *
  • string != null + *
  • string.length() > 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseString(String string) + throws XMLParseException + { + try { + this.parseFromReader(new StringReader(string), + /*startingLineNr*/ 1); + } catch (IOException e) { + // Java exception handling suxx + } + } + + + /** + * Reads one XML element from a String and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param offset + * The first character in string to scan. + * + *
Preconditions:
+ *
  • string != null + *
  • offset < string.length() + *
  • offset >= 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseString(String string, + int offset) + throws XMLParseException + { + this.parseString(string.substring(offset)); + } + + + /** + * Reads one XML element from a String and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param offset + * The first character in string to scan. + * @param end + * The character where to stop scanning. + * This character is not scanned. + * + *
Preconditions:
+ *
  • string != null + *
  • end <= string.length() + *
  • offset < end + *
  • offset >= 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseString(String string, + int offset, + int end) + throws XMLParseException + { + this.parseString(string.substring(offset, end)); + } + + + /** + * Reads one XML element from a String and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param offset + * The first character in string to scan. + * @param end + * The character where to stop scanning. + * This character is not scanned. + * @param startingLineNr + * The line number of the first line in the data. + * + *
Preconditions:
+ *
  • string != null + *
  • end <= string.length() + *
  • offset < end + *
  • offset >= 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseString(String string, + int offset, + int end, + int startingLineNr) + throws XMLParseException + { + string = string.substring(offset, end); + try { + this.parseFromReader(new StringReader(string), startingLineNr); + } catch (IOException e) { + // Java exception handling suxx + } + } + + + /** + * Reads one XML element from a char array and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param offset + * The first character in string to scan. + * @param end + * The character where to stop scanning. + * This character is not scanned. + * + *
Preconditions:
+ *
  • input != null + *
  • end <= input.length + *
  • offset < end + *
  • offset >= 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseCharArray(char[] input, + int offset, + int end) + throws XMLParseException + { + this.parseCharArray(input, offset, end, /*startingLineNr*/ 1); + } + + + /** + * Reads one XML element from a char array and parses it. + * + * @param reader + * The reader from which to retrieve the XML data. + * @param offset + * The first character in string to scan. + * @param end + * The character where to stop scanning. + * This character is not scanned. + * @param startingLineNr + * The line number of the first line in the data. + * + *
Preconditions:
+ *
  • input != null + *
  • end <= input.length + *
  • offset < end + *
  • offset >= 0 + *
+ * + *
Postconditions:
+ *
  • the state of the receiver is updated to reflect the XML element + * parsed from the reader + *
+ * + * @throws nanoxml.XMLParseException + * If an error occured while parsing the string. + */ + public void parseCharArray(char[] input, + int offset, + int end, + int startingLineNr) + throws XMLParseException + { + try { + Reader reader = new CharArrayReader(input, offset, end); + this.parseFromReader(reader, startingLineNr); + } catch (IOException e) { + // This exception will never happen. + } + } + + + /** + * Removes a child element. + * + * @param child + * The child element to remove. + * + *
Preconditions:
+ *
  • child != null + *
  • child is a child element of the receiver + *
+ * + *
Postconditions:
+ *
  • countChildren() => old.countChildren() - 1 + *
  • enumerateChildren() => old.enumerateChildren() - child + *
  • getChildren() => old.enumerateChildren() - child + *
+ * + * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) + * addChild(XMLElement) + * @see nanoxml.XMLElement#countChildren() + * @see nanoxml.XMLElement#enumerateChildren() + * @see nanoxml.XMLElement#getChildren() + */ + public void removeChild(XMLElement child) + { + this.children.removeElement(child); + } + + + /** + * Removes an attribute. + * + * @param name + * The name of the attribute. + * + *
Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + *
Postconditions:
+ *
  • enumerateAttributeNames() + * => old.enumerateAttributeNames() - name + *
  • getAttribute(name) => null + *
+ * + * @see nanoxml.XMLElement#enumerateAttributeNames() + * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double) + * setDoubleAttribute(String, double) + * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int) + * setIntAttribute(String, int) + * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object) + * setAttribute(String, Object) + * @see nanoxml.XMLElement#getAttribute(java.lang.String) + * getAttribute(String) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object) + * getAttribute(String, Object) + * @see nanoxml.XMLElement#getAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String) + * getStringAttribute(String) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.lang.String) + * getStringAttribute(String, String) + * @see nanoxml.XMLElement#getStringAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getStringAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String) + * getIntAttribute(String) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int) + * getIntAttribute(String, int) + * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getIntAttribute(String, Hashtable, String, boolean) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String) + * getDoubleAttribute(String) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double) + * getDoubleAttribute(String, double) + * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, + * java.util.Hashtable, + * java.lang.String, boolean) + * getDoubleAttribute(String, Hashtable, String, boolean) + * @see nanoxml.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(); + } + this.attributes.remove(name); + } + + + /** + * Removes an attribute. + * + * @param name + * 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. + * + * @param name + * 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. + *

+ * You should override this method when subclassing XMLElement. + */ + protected XMLElement createAnotherElement() + { + return new XMLElement(this.entities, + this.ignoreWhitespace, + false, + this.ignoreCase); + } + + + /** + * Changes the content string. + * + * @param content + * The new content string. + */ + public void setContent(String content) + { + this.contents = content; + } + + + /** + * Changes the name of the element. + * + * @param name + * The new name. + * + * @deprecated Use {@link #setName(java.lang.String) setName} instead. + */ + public void setTagName(String name) + { + this.setName(name); + } + + + /** + * Changes the name of the element. + * + * @param name + * The new name. + * + *

Preconditions:
+ *
  • name != null + *
  • name is a valid XML identifier + *
+ * + * @see nanoxml.XMLElement#getName() + */ + public void setName(String name) + { + this.name = name; + } + + + /** + * Writes the XML element to a string. + * + * @see nanoxml.XMLElement#write(java.io.Writer) write(Writer) + */ + public String toString() + { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out); + this.write(writer); + writer.flush(); + return new String(out.toByteArray()); + } catch (IOException e) { + // Java exception handling suxx + return super.toString(); + } + } + + + /** + * Writes the XML element to a writer. + * + * @param writer + * The writer to write the XML data to. + * + *
Preconditions:
+ *
  • writer != null + *
  • writer is not closed + *
+ * + * @throws java.io.IOException + * If the data could not be written to the writer. + * + * @see nanoxml.XMLElement#toString() + */ + public void write(Writer writer) + throws IOException + { + if (this.name == null) { + this.writeEncoded(writer, this.contents); + return; + } + writer.write('<'); + writer.write(this.name); + if (! this.attributes.isEmpty()) { + Enumeration enumV = this.attributes.keys(); + while (enumV.hasMoreElements()) { + writer.write(' '); + String key = (String) enumV.nextElement(); + String value = (String) this.attributes.get(key); + writer.write(key); + writer.write('='); writer.write('"'); + this.writeEncoded(writer, value); + writer.write('"'); + } + } + if ((this.contents != null) && (this.contents.length() > 0)) { + writer.write('>'); + this.writeEncoded(writer, this.contents); + writer.write('<'); writer.write('/'); + writer.write(this.name); + writer.write('>'); + } else if (this.children.isEmpty()) { + writer.write('/'); writer.write('>'); + } else { + writer.write('>'); + Enumeration enumV = this.enumerateChildren(); + while (enumV.hasMoreElements()) { + XMLElement child = (XMLElement) enumV.nextElement(); + child.write(writer); + } + writer.write('<'); writer.write('/'); + writer.write(this.name); + writer.write('>'); + } + } + + + /** + * Writes a string encoded to a writer. + * + * @param writer + * The writer to write the XML data to. + * @param str + * The string to write encoded. + * + *
Preconditions:
+ *
  • writer != null + *
  • writer is not closed + *
  • str != null + *
+ */ + protected void writeEncoded(Writer writer, + String str) + throws IOException + { + for (int i = 0; i < str.length(); i += 1) { + char ch = str.charAt(i); + switch (ch) { + case '<': + writer.write('&'); writer.write('l'); writer.write('t'); + writer.write(';'); + break; + case '>': + writer.write('&'); writer.write('g'); writer.write('t'); + writer.write(';'); + break; + case '&': + writer.write('&'); writer.write('a'); writer.write('m'); + writer.write('p'); writer.write(';'); + break; + case '"': + writer.write('&'); writer.write('q'); writer.write('u'); + writer.write('o'); writer.write('t'); writer.write(';'); + break; + case '\'': + writer.write('&'); writer.write('a'); writer.write('p'); + writer.write('o'); writer.write('s'); writer.write(';'); + break; + default: + int unicode = (int) ch; + if ((unicode < 32) || (unicode > 126)) { + writer.write('&'); writer.write('#'); + writer.write('x'); + writer.write(Integer.toString(unicode, 16)); + writer.write(';'); + } else { + writer.write(ch); + } + } + } + } + + + /** + * Scans an identifier from the current reader. + * The scanned identifier is appended to result. + * + * @param result + * The buffer in which the scanned identifier will be put. + * + *
Preconditions:
+ *
  • result != null + *
  • The next character read from the reader is a valid first + * character of an XML identifier. + *
+ * + *
Postconditions:
+ *
  • The next character read from the reader won't be an identifier + * character. + *
+ */ + protected void scanIdentifier(StringBuffer result) + throws IOException + { + for (;;) { + char ch = this.readChar(); + if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z')) + && ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.') + && (ch != ':') && (ch != '-') && (ch <= '\u007E')) { + this.unreadChar(ch); + return; + } + result.append(ch); + } + } + + + /** + * This method scans an identifier from the current reader. + * + * @return the next character following the whitespace. + */ + protected char scanWhitespace() + throws IOException + { + for (;;) { + char ch = this.readChar(); + switch (ch) { + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + return ch; + } + } + } + + + /** + * This method scans an identifier from the current reader. + * The scanned whitespace is appended to result. + * + * @return the next character following the whitespace. + * + *
Preconditions:
+ *
  • result != null + *
+ */ + protected char scanWhitespace(StringBuffer result) + throws IOException + { + for (;;) { + char ch = this.readChar(); + switch (ch) { + case ' ': + case '\t': + case '\n': + result.append(ch); + case '\r': + break; + default: + return ch; + } + } + } + + + /** + * This method scans a delimited string from the current reader. + * The scanned string without delimiters is appended to + * string. + * + *
Preconditions:
+ *
  • string != null + *
  • the next char read is the string delimiter + *
+ */ + protected void scanString(StringBuffer string) + throws IOException + { + char delimiter = this.readChar(); + if ((delimiter != '\'') && (delimiter != '"')) { + throw this.expectedInput("' or \""); + } + for (;;) { + char ch = this.readChar(); + if (ch == delimiter) { + return; + } else if (ch == '&') { + this.resolveEntity(string); + } else { + string.append(ch); + } + } + } + + + /** + * Scans a #PCDATA element. CDATA sections and entities are resolved. + * The next < char is skipped. + * The scanned data is appended to data. + * + *
Preconditions:
+ *
  • data != null + *
+ */ + protected void scanPCData(StringBuffer data) + throws IOException + { + for (;;) { + char ch = this.readChar(); + if (ch == '<') { + ch = this.readChar(); + if (ch == '!') { + this.checkCDATA(data); + } else { + this.unreadChar(ch); + return; + } + } else if (ch == '&') { + this.resolveEntity(data); + } else { + data.append(ch); + } + } + } + + + /** + * Scans a special tag and if the tag is a CDATA section, append its + * content to buf. + * + *
Preconditions:
+ *
  • buf != null + *
  • The first < has already been read. + *
+ */ + protected boolean checkCDATA(StringBuffer buf) + throws IOException + { + char ch = this.readChar(); + if (ch != '[') { + this.unreadChar(ch); + this.skipSpecialTag(0); + return false; + } else if (! this.checkLiteral("CDATA[")) { + this.skipSpecialTag(1); // one [ has already been read + return false; + } else { + int delimiterCharsSkipped = 0; + while (delimiterCharsSkipped < 3) { + ch = this.readChar(); + switch (ch) { + case ']': + if (delimiterCharsSkipped < 2) { + delimiterCharsSkipped += 1; + } else { + buf.append(']'); + buf.append(']'); + delimiterCharsSkipped = 0; + } + break; + case '>': + if (delimiterCharsSkipped < 2) { + for (int i = 0; i < delimiterCharsSkipped; i++) { + buf.append(']'); + } + delimiterCharsSkipped = 0; + buf.append('>'); + } else { + delimiterCharsSkipped = 3; + } + break; + default: + for (int i = 0; i < delimiterCharsSkipped; i += 1) { + buf.append(']'); + } + buf.append(ch); + delimiterCharsSkipped = 0; + } + } + return true; + } + } + + + /** + * Skips a comment. + * + *
Preconditions:
+ *
  • The first <!-- has already been read. + *
+ */ + protected void skipComment() + throws IOException + { + int dashesToRead = 2; + while (dashesToRead > 0) { + char ch = this.readChar(); + if (ch == '-') { + dashesToRead -= 1; + } else { + dashesToRead = 2; + } + } + if (this.readChar() != '>') { + throw this.expectedInput(">"); + } + } + + + /** + * Skips a special tag or comment. + * + * @param bracketLevel The number of open square brackets ([) that have + * already been read. + * + *
Preconditions:
+ *
  • The first <! has already been read. + *
  • bracketLevel >= 0 + *
+ */ + protected void skipSpecialTag(int bracketLevel) + throws IOException + { + int tagLevel = 1; // < + char stringDelimiter = '\0'; + if (bracketLevel == 0) { + char ch = this.readChar(); + if (ch == '[') { + bracketLevel += 1; + } else if (ch == '-') { + ch = this.readChar(); + if (ch == '[') { + bracketLevel += 1; + } else if (ch == ']') { + bracketLevel -= 1; + } else if (ch == '-') { + this.skipComment(); + return; + } + } + } + while (tagLevel > 0) { + char ch = this.readChar(); + if (stringDelimiter == '\0') { + if ((ch == '"') || (ch == '\'')) { + stringDelimiter = ch; + } else if (bracketLevel <= 0) { + if (ch == '<') { + tagLevel += 1; + } else if (ch == '>') { + tagLevel -= 1; + } + } + if (ch == '[') { + bracketLevel += 1; + } else if (ch == ']') { + bracketLevel -= 1; + } + } else { + if (ch == stringDelimiter) { + stringDelimiter = '\0'; + } + } + } + } + + + /** + * Scans the data for literal text. + * Scanning stops when a character does not match or after the complete + * text has been checked, whichever comes first. + * + * @param literal the literal to check. + * + *
Preconditions:
+ *
  • literal != null + *
+ */ + protected boolean checkLiteral(String literal) + throws IOException + { + int length = literal.length(); + for (int i = 0; i < length; i += 1) { + if (this.readChar() != literal.charAt(i)) { + return false; + } + } + return true; + } + + + /** + * Reads a character from a reader. + */ + protected char readChar() + throws IOException + { + if (this.charReadTooMuch != '\0') { + char ch = this.charReadTooMuch; + this.charReadTooMuch = '\0'; + return ch; + } else { + int i = this.reader.read(); + if (i < 0) { + throw this.unexpectedEndOfData(); + } else if (i == 10) { + this.parserLineNr += 1; + return '\n'; + } else { + return (char) i; + } + } + } + + + /** + * Scans an XML element. + * + * @param elt The element that will contain the result. + * + *
Preconditions:
+ *
  • The first < has already been read. + *
  • elt != null + *
+ */ + protected void scanElement(XMLElement elt) + throws IOException + { + StringBuffer buf = new StringBuffer(); + this.scanIdentifier(buf); + String name = buf.toString(); + elt.setName(name); + char ch = this.scanWhitespace(); + while ((ch != '>') && (ch != '/')) { + buf.setLength(0); + this.unreadChar(ch); + this.scanIdentifier(buf); + String key = buf.toString(); + ch = this.scanWhitespace(); + if (ch != '=') { + throw this.expectedInput("="); + } + this.unreadChar(this.scanWhitespace()); + buf.setLength(0); + this.scanString(buf); + elt.setAttribute(key, buf); + ch = this.scanWhitespace(); + } + if (ch == '/') { + ch = this.readChar(); + if (ch != '>') { + throw this.expectedInput(">"); + } + return; + } + buf.setLength(0); + ch = this.scanWhitespace(buf); + if (ch != '<') { + this.unreadChar(ch); + this.scanPCData(buf); + } else { + for (;;) { + ch = this.readChar(); + if (ch == '!') { + if (this.checkCDATA(buf)) { + this.scanPCData(buf); + break; + } else { + ch = this.scanWhitespace(buf); + if (ch != '<') { + this.unreadChar(ch); + this.scanPCData(buf); + break; + } + } + } else { + if ((ch != '/') || this.ignoreWhitespace) { + buf.setLength(0); + } + if (ch == '/') { + this.unreadChar(ch); + } + break; + } + } + } + if (buf.length() == 0) { + while (ch != '/') { + if (ch == '!') { + ch = this.readChar(); + if (ch != '-') { + throw this.expectedInput("Comment or Element"); + } + ch = this.readChar(); + if (ch != '-') { + throw this.expectedInput("Comment or Element"); + } + this.skipComment(); + } else { + this.unreadChar(ch); + XMLElement child = this.createAnotherElement(); + this.scanElement(child); + elt.addChild(child); + } + ch = this.scanWhitespace(); + if (ch != '<') { + throw this.expectedInput("<"); + } + ch = this.readChar(); + } + this.unreadChar(ch); + } else { + if (this.ignoreWhitespace) { + elt.setContent(buf.toString().trim()); + } else { + elt.setContent(buf.toString()); + } + } + ch = this.readChar(); + if (ch != '/') { + throw this.expectedInput("/"); + } + this.unreadChar(this.scanWhitespace()); + if (! this.checkLiteral(name)) { + throw this.expectedInput(name); + } + if (this.scanWhitespace() != '>') { + throw this.expectedInput(">"); + } + } + + + /** + * Resolves an entity. The name of the entity is read from the reader. + * The value of the entity is appended to buf. + * + * @param buf Where to put the entity value. + * + *
Preconditions:
+ *
  • The first & has already been read. + *
  • buf != null + *
+ */ + protected void resolveEntity(StringBuffer buf) + throws IOException + { + char ch = '\0'; + StringBuffer keyBuf = new StringBuffer(); + for (;;) { + ch = this.readChar(); + if (ch == ';') { + break; + } + keyBuf.append(ch); + } + String key = keyBuf.toString(); + if (key.charAt(0) == '#') { + try { + if (key.charAt(1) == 'x') { + ch = (char) Integer.parseInt(key.substring(2), 16); + } else { + ch = (char) Integer.parseInt(key.substring(1), 10); + } + } catch (NumberFormatException e) { + throw this.unknownEntity(key); + } + buf.append(ch); + } else { + char[] value = (char[]) this.entities.get(key); + if (value == null) { + throw this.unknownEntity(key); + } + buf.append(value); + } + } + + + /** + * Pushes a character back to the read-back buffer. + * + * @param ch The character to push back. + * + *
Preconditions:
+ *
  • The read-back buffer is empty. + *
  • ch != '\0' + *
+ */ + protected void unreadChar(char ch) + { + this.charReadTooMuch = ch; + } + + + /** + * Creates a parse exception for when an invalid valueset is given to + * a method. + * + * @param name The name of the entity. + * + *
Preconditions:
+ *
  • name != null + *
+ */ + protected XMLParseException invalidValueSet(String name) + { + String msg = "Invalid value set (entity name = \"" + name + "\")"; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + + + /** + * Creates a parse exception for when an invalid value is given to a + * method. + * + * @param name The name of the entity. + * @param value The value of the entity. + * + *
Preconditions:
+ *
  • name != null + *
  • value != null + *
+ */ + protected XMLParseException invalidValue(String name, + String value) + { + String msg = "Attribute \"" + name + "\" does not contain a valid " + + "value (\"" + value + "\")"; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + + + /** + * Creates a parse exception for when the end of the data input has been + * reached. + */ + protected XMLParseException unexpectedEndOfData() + { + String msg = "Unexpected end of data reached"; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + + + /** + * Creates a parse exception for when a syntax error occured. + * + * @param context The context in which the error occured. + * + *
Preconditions:
+ *
  • context != null + *
  • context.length() > 0 + *
+ */ + protected XMLParseException syntaxError(String context) + { + String msg = "Syntax error while parsing " + context; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + + + /** + * Creates a parse exception for when the next character read is not + * the character that was expected. + * + * @param charSet The set of characters (in human readable form) that was + * expected. + * + *
Preconditions:
+ *
  • charSet != null + *
  • charSet.length() > 0 + *
+ */ + protected XMLParseException expectedInput(String charSet) + { + String msg = "Expected: " + charSet; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + + + /** + * Creates a parse exception for when an entity could not be resolved. + * + * @param name The name of the entity. + * + *
Preconditions:
+ *
  • name != null + *
  • name.length() > 0 + *
+ */ + protected XMLParseException unknownEntity(String name) + { + String msg = "Unknown or invalid entity: &" + name + ";"; + return new XMLParseException(this.getName(), this.parserLineNr, msg); + } + +} diff -r 6dcea85ad8ba -r 75acc009e9d8 src/share/classes/sun/util/xml/XMLParseException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/util/xml/XMLParseException.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,129 @@ +/* XMLParseException.java + * + * $Revision: 1.4 $ + * $Date: 2002/03/24 10:27:59 $ + * $Name: RELEASE_2_2_1 $ + * + * This file is part of NanoXML 2 Lite. + * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + *****************************************************************************/ + + + package sun.util.xml; + + +/** + * An XMLParseException is thrown when an error occures while parsing an XML + * string. + *

+ * $Revision: 1.4 $
+ * $Date: 2002/03/24 10:27:59 $

+ * + * @see nanoxml.XMLElement + * + * @author Marc De Scheemaecker + * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $ + */ +class XMLParseException extends RuntimeException +{ + + /** + * Indicates that no line number has been associated with this exception. + */ + public static final int NO_LINE = -1; + + + /** + * The line number in the source code where the error occurred, or + * NO_LINE if the line number is unknown. + * + *

Invariants:
+ *
  • lineNr > 0 || lineNr == NO_LINE + *
+ */ + private int lineNr; + + + /** + * Creates an exception. + * + * @param name The name of the element where the error is located. + * @param message A message describing what went wrong. + * + *
Preconditions:
+ *
  • message != null + *
+ * + *
Postconditions:
+ *
  • getLineNr() => NO_LINE + *
+ */ + public XMLParseException(String name, + String message) + { + super("XML Parse Exception during parsing of " + + ((name == null) ? "the XML definition" + : ("a " + name + " element")) + + ": " + message); + this.lineNr = XMLParseException.NO_LINE; + } + + + /** + * Creates an exception. + * + * @param name The name of the element where the error is located. + * @param lineNr The number of the line in the input. + * @param message A message describing what went wrong. + * + *
Preconditions:
+ *
  • message != null + *
  • lineNr > 0 + *
+ * + *
Postconditions:
+ *
  • getLineNr() => lineNr + *
+ */ + public XMLParseException(String name, + int lineNr, + String message) + { + super("XML Parse Exception during parsing of " + + ((name == null) ? "the XML definition" + : ("a " + name + " element")) + + " at line " + lineNr + ": " + message); + this.lineNr = lineNr; + } + + + /** + * Where the error occurred, or NO_LINE if the line number is + * unknown. + * + * @see nanoxml.XMLParseException#NO_LINE + */ + public int getLineNr() + { + return this.lineNr; + } + +} diff -r 6dcea85ad8ba -r 75acc009e9d8 src/solaris/classes/java/util/prefs/FileSystemPreferences.java --- a/src/solaris/classes/java/util/prefs/FileSystemPreferences.java Mon Jun 22 18:07:11 2009 +0200 +++ b/src/solaris/classes/java/util/prefs/FileSystemPreferences.java Wed Jun 24 17:51:59 2009 +0200 @@ -572,7 +572,7 @@ try { newLastSyncTime = prefsFile.lastModified(); FileInputStream fis = new FileInputStream(prefsFile); - XmlSupport.importMap(fis, m); + sun.util.xml.PrefsXmlSupport.getDefault().importMap(fis, m); fis.close(); } catch(Exception e) { if (e instanceof InvalidPreferencesFormatException) { @@ -619,7 +619,7 @@ throw new BackingStoreException(dir + " create failed."); FileOutputStream fos = new FileOutputStream(tmpFile); - XmlSupport.exportMap(fos, prefsCache); + sun.util.xml.PrefsXmlSupport.getDefault().exportMap(fos, prefsCache); fos.close(); if (!tmpFile.renameTo(prefsFile)) throw new BackingStoreException("Can't rename " + diff -r 6dcea85ad8ba -r 75acc009e9d8 test/java/util/Properties/XMLReadAndWriteTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Properties/XMLReadAndWriteTest.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,83 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +import java.io.ByteArrayInputStream; +import sun.util.xml.PropertiesXMLUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** Checks whether reading and writing via standard DOM and simplified API + * results in same results. + * + * @author Jaroslav Tulach + */ +public class XMLReadAndWriteTest { + private static PropertiesXMLUtils FULL = new com.sun.xml.internal.PropertiesXMLUtilsImpl(); + private static PropertiesXMLUtils SIMPLE = new sun.util.xml.DefaultPropertiesXMLUtils(); + + + public static void main(String[] args) throws Exception { + XMLReadAndWriteTest test = new XMLReadAndWriteTest(); + test.testCompareOutput(); + test.testCompareInput(); + } + + + public XMLReadAndWriteTest() { + } + + public void testCompareOutput() throws IOException { + Properties p = new Properties(); + p.setProperty("ahoj", "simple"); + p.setProperty("kuk", "buk"); + p.setProperty("multi", "one\ntwo\nthree\nfour"); + + ByteArrayOutputStream full = new ByteArrayOutputStream(); + FULL.save(p, full, "my commment on beginging\nand on the second line\n", "UTF-8"); + + ByteArrayOutputStream simple = new ByteArrayOutputStream(); + SIMPLE.save(p, simple, "my commment on beginging\nand on the second line\n", "UTF-8"); + if (full.toString().equals(simple.toString())) { + // OK + System.err.println("OK: testCompareOutput"); + } else { + assert false : + "Full version differs from simplified. Full:\n" + full + "\nSimple:\n" + simple; + } + } + + public void testCompareInput() throws IOException { + String text = "" + + " " + + "\n" + + "my commment on beginging\n" + + "and on the second line" + + "" + + "simple\n" + + " buk" + + " one\n" + + "two\n" + + "three\n" + + "four\n"; + + Properties p1 = new Properties(); + { + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8")); + FULL.load(p1, is); + } + + Properties p2 = new Properties(); + { + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8")); + SIMPLE.load(p2, is); + } + + assert p1.equals(p2) : "P1: " + p1 + "\nP2: " + p2; + System.err.println("OK: testCompareInput"); + } + +} \ No newline at end of file diff -r 6dcea85ad8ba -r 75acc009e9d8 test/java/util/prefs/Preferences/XMLPreferencesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/prefs/Preferences/XMLPreferencesTest.java Wed Jun 24 17:51:59 2009 +0200 @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + + +import java.io.ByteArrayInputStream; +import sun.util.xml.PrefsXmlSupport; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.prefs.Preferences; + +/** Checks whether reading and writing via standard DOM and simplified API + * results in same results. + * + * @author Jaroslav Tulach + */ +public class XMLPreferencesTest { + private static PrefsXmlSupport FULL = new com.sun.xml.internal.PrefsXmlSupportImpl(); + private static PrefsXmlSupport SIMPLE = new sun.util.xml.DefaultPrefsXmlSupport(); + + + public static void main(String[] args) throws Exception { + XMLPreferencesTest test = new XMLPreferencesTest(); + test.testCompareOutput(); + test.testCompareInput(); + } + + + public XMLPreferencesTest() { + } + + public void testCompareOutput() throws Exception { + Preferences p = Preferences.userRoot().node("a/b/c"); + p.put("ahoj", "simple"); + p.putInt("kuk", 1); + p.putBoolean("multi", true); + p.node("child").putBoolean("data", false); + p.node("empty"); + p.parent().putDouble("visible", 1.0); + + ByteArrayOutputStream full = new ByteArrayOutputStream(); + FULL.export(full, p, true); + + ByteArrayOutputStream simple = new ByteArrayOutputStream(); + SIMPLE.export(simple, p, true); + if (full.toString().equals(simple.toString())) { + // OK + System.err.println("OK: testCompareOutput"); + } else { + assert false : + "Full version differs from simplified. Full:\n" + full + "\nSimple:\n" + simple; + } + } + + public void testCompareInput() throws Exception { + Preferences.userRoot().node("test1/b/c").removeNode(); + Preferences.userRoot().node("test2/b/c").removeNode(); + + String text = "" + + "" + + " " + + " \n\n\n" + + "" + + "" + + " " + + " " + + " \n" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + { + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8")); + FULL.importPreferences(is); + } + + { + text = text.replace("'test1'", "'test2'"); + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8")); + SIMPLE.importPreferences(is); + } + + assert Preferences.userRoot().nodeExists("test1/b/c") : "Node in user imported"; + assert Preferences.userRoot().nodeExists("test2/b/c") : "Node in system imported"; + + Preferences u = Preferences.userRoot().node("test1/b/c"); + Preferences s = Preferences.userRoot().node("test2/b/c"); + + assert Arrays.equals(u.keys(), s.keys()) : "Same keys in both nodes"; + + for (String k : u.keys()) { + assert u.get(k, "Neco").equals(s.get(k, "Cone")) : "Same values for " + k; + } + System.err.println("OK: testCompareInput"); + } + +} \ No newline at end of file