jtulach@1263: /*
jtulach@1263: * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
jtulach@1263: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jtulach@1263: *
jtulach@1263: * This code is free software; you can redistribute it and/or modify it
jtulach@1263: * under the terms of the GNU General Public License version 2 only, as
jtulach@1263: * published by the Free Software Foundation. Sun designates this
jtulach@1263: * particular file as subject to the "Classpath" exception as provided
jtulach@1263: * by Sun in the LICENSE file that accompanied this code.
jtulach@1263: *
jtulach@1263: * This code is distributed in the hope that it will be useful, but WITHOUT
jtulach@1263: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jtulach@1263: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jtulach@1263: * version 2 for more details (a copy is included in the LICENSE file that
jtulach@1263: * accompanied this code).
jtulach@1263: *
jtulach@1263: * You should have received a copy of the GNU General Public License version
jtulach@1263: * 2 along with this work; if not, write to the Free Software Foundation,
jtulach@1263: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jtulach@1263: *
jtulach@1263: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
jtulach@1263: * CA 95054 USA or visit www.sun.com if you need additional information or
jtulach@1263: * have any questions.
jtulach@1263: */
jtulach@1263:
jtulach@1263: package sun.util.xml;
jtulach@1263:
jtulach@1263: import java.util.*;
jtulach@1263: import java.util.prefs.*;
jtulach@1263: import java.io.*;
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Simplified XML Support for java.util.prefs. Methods to import and export preference
jtulach@1263: * nodes and subtrees.
jtulach@1263: *
jtulach@1263: * @author Jaroslav Tulach
jtulach@1263: * @since 1.7
jtulach@1263: */
jtulach@1263: public class DefaultPrefsXmlSupport extends sun.util.xml.PrefsXmlSupport {
jtulach@1263: public void export(OutputStream os, final Preferences p, boolean subTree)
jtulach@1263: throws IOException, BackingStoreException {
jtulach@1263: if (isRemoved(p))
jtulach@1263: throw new IllegalStateException("Node has been removed");
jtulach@1263: PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
jtulach@1263: w.println("");
jtulach@1263: w.println("");
jtulach@1263: w.println("");
jtulach@1263: w.print(" ");
jtulach@1263: LinkedList ancestors = new LinkedList();
jtulach@1263: for (Preferences kid = p, dad = kid.parent(); dad != null;
jtulach@1263: kid = dad, dad = kid.parent()) {
jtulach@1263: ancestors.addFirst(kid);
jtulach@1263: }
jtulach@1263: String indent = " ";
jtulach@1263: for (Preferences pref : ancestors) {
jtulach@1263: indent = " " + indent;
jtulach@1263: w.print(indent); w.println("");
jtulach@1263: w.print(indent); w.print("");
jtulach@1263: }
jtulach@1263:
jtulach@1263: putPreferencesInXml(w, indent + " ", p, subTree);
jtulach@1263: for (Preferences pref : ancestors) {
jtulach@1263: w.print(indent); w.println("");
jtulach@1263: indent = indent.substring(2);
jtulach@1263: }
jtulach@1263:
jtulach@1263: w.println(" ");
jtulach@1263: w.println("");
jtulach@1263: w.flush();
jtulach@1263: }
jtulach@1263:
jtulach@1263: private static void putPreferencesInXml(
jtulach@1263: PrintWriter w, String indent, Preferences prefs, boolean subTree
jtulach@1263: ) throws BackingStoreException {
jtulach@1263: Preferences[] kidsCopy = null;
jtulach@1263: String[] kidNames = null;
jtulach@1263:
jtulach@1263: // Node is locked to export its contents and get a
jtulach@1263: // copy of children, then lock is released,
jtulach@1263: // and, if subTree = true, recursive calls are made on children
jtulach@1263: synchronized (lock(prefs)) {
jtulach@1263: // Check if this node was concurrently removed. If yes
jtulach@1263: // don't print it
jtulach@1263: if (isRemoved(prefs)) {
jtulach@1263: return;
jtulach@1263: }
jtulach@1263: // Put map in xml element
jtulach@1263: String[] keys = prefs.keys();
jtulach@1263: if (keys.length == 0) {
jtulach@1263: w.print(indent); w.println("");
jtulach@1263: } else {
jtulach@1263: w.print(indent); w.println("");
jtulach@1263: }
jtulach@1263: // Recurse if appropriate
jtulach@1263: if (subTree) {
jtulach@1263: /* Get a copy of kids while lock is held */
jtulach@1263: kidNames = prefs.childrenNames();
jtulach@1263: kidsCopy = new Preferences[kidNames.length];
jtulach@1263: for (int i = 0; i < kidNames.length; i++)
jtulach@1263: kidsCopy[i] = prefs.node(kidNames[i]);
jtulach@1263: }
jtulach@1263: // release lock
jtulach@1263: }
jtulach@1263:
jtulach@1263: if (subTree) {
jtulach@1263: for (int i=0; i < kidNames.length; i++) {
jtulach@1263: w.print(indent); w.print("");
jtulach@1263: putPreferencesInXml(w, " " + indent, kidsCopy[i], subTree);
jtulach@1263: w.print(indent); w.println("");
jtulach@1263: }
jtulach@1263: }
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Import preferences from the specified input stream, which is assumed
jtulach@1263: * to contain an XML document in the format described in the Preferences
jtulach@1263: * spec.
jtulach@1263: *
jtulach@1263: * @throws IOException if reading from the specified output stream
jtulach@1263: * results in an IOException.
jtulach@1263: * @throws InvalidPreferencesFormatException Data on input stream does not
jtulach@1263: * constitute a valid XML document with the mandated document type.
jtulach@1263: */
jtulach@1263: public void importPreferences(InputStream is)
jtulach@1263: throws IOException, InvalidPreferencesFormatException
jtulach@1263: {
jtulach@1263: /*
jtulach@1263: try {
jtulach@1263: Document doc = loadPrefsDoc(is);
jtulach@1263: String xmlVersion =
jtulach@1263: doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION");
jtulach@1263: if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
jtulach@1263: throw new InvalidPreferencesFormatException(
jtulach@1263: "Exported preferences file format version " + xmlVersion +
jtulach@1263: " is not supported. This java installation can read" +
jtulach@1263: " versions " + EXTERNAL_XML_VERSION + " or older. You may need" +
jtulach@1263: " to install a newer version of JDK.");
jtulach@1263:
jtulach@1263: Element xmlRoot = (Element) doc.getDocumentElement().
jtulach@1263: getChildNodes().item(0);
jtulach@1263: Preferences prefsRoot =
jtulach@1263: (xmlRoot.getAttribute("type").equals("user") ?
jtulach@1263: Preferences.userRoot() : Preferences.systemRoot());
jtulach@1263: ImportSubtree(prefsRoot, xmlRoot);
jtulach@1263: } catch(SAXException e) {
jtulach@1263: throw new InvalidPreferencesFormatException(e);
jtulach@1263: }
jtulach@1263: */
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Create a new prefs XML document.
jtulach@1263: *
jtulach@1263: private static Document createPrefsDoc( String qname ) {
jtulach@1263: try {
jtulach@1263: DOMImplementation di = DocumentBuilderFactory.newInstance().
jtulach@1263: newDocumentBuilder().getDOMImplementation();
jtulach@1263: DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI);
jtulach@1263: return di.createDocument(null, qname, dt);
jtulach@1263: } catch(ParserConfigurationException e) {
jtulach@1263: throw new AssertionError(e);
jtulach@1263: }
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Load an XML document from specified input stream, which must
jtulach@1263: * have the requisite DTD URI.
jtulach@1263: *
jtulach@1263: private static Document loadPrefsDoc(InputStream in)
jtulach@1263: throws SAXException, IOException
jtulach@1263: {
jtulach@1263: DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
jtulach@1263: dbf.setIgnoringElementContentWhitespace(true);
jtulach@1263: dbf.setValidating(true);
jtulach@1263: dbf.setCoalescing(true);
jtulach@1263: dbf.setIgnoringComments(true);
jtulach@1263: try {
jtulach@1263: DocumentBuilder db = dbf.newDocumentBuilder();
jtulach@1263: db.setEntityResolver(new Resolver());
jtulach@1263: db.setErrorHandler(new EH());
jtulach@1263: return db.parse(new InputSource(in));
jtulach@1263: } catch (ParserConfigurationException e) {
jtulach@1263: throw new AssertionError(e);
jtulach@1263: }
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Write XML document to the specified output stream.
jtulach@1263: *
jtulach@1263: private static final void writeDoc(Document doc, OutputStream out)
jtulach@1263: throws IOException
jtulach@1263: {
jtulach@1263: try {
jtulach@1263: TransformerFactory tf = TransformerFactory.newInstance();
jtulach@1263: try {
jtulach@1263: tf.setAttribute("indent-number", new Integer(2));
jtulach@1263: } catch (IllegalArgumentException iae) {
jtulach@1263: //Ignore the IAE. Should not fail the writeout even the
jtulach@1263: //transformer provider does not support "indent-number".
jtulach@1263: }
jtulach@1263: Transformer t = tf.newTransformer();
jtulach@1263: t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());
jtulach@1263: t.setOutputProperty(OutputKeys.INDENT, "yes");
jtulach@1263: //Transformer resets the "indent" info if the "result" is a StreamResult with
jtulach@1263: //an OutputStream object embedded, creating a Writer object on top of that
jtulach@1263: //OutputStream object however works.
jtulach@1263: t.transform(new DOMSource(doc),
jtulach@1263: new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))));
jtulach@1263: } catch(TransformerException e) {
jtulach@1263: throw new AssertionError(e);
jtulach@1263: }
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Recursively traverse the specified preferences node and store
jtulach@1263: * the described preferences into the system or current user
jtulach@1263: * preferences tree, as appropriate.
jtulach@1263: *
jtulach@1263: private static void ImportSubtree(Preferences prefsNode, Element xmlNode) {
jtulach@1263: NodeList xmlKids = xmlNode.getChildNodes();
jtulach@1263: int numXmlKids = xmlKids.getLength();
jtulach@1263: /*
jtulach@1263: * We first lock the node, import its contents and get
jtulach@1263: * child nodes. Then we unlock the node and go to children
jtulach@1263: * Since some of the children might have been concurrently
jtulach@1263: * deleted we check for this.
jtulach@1263: * /
jtulach@1263: Preferences[] prefsKids;
jtulach@1263: /* Lock the node * /
jtulach@1263: synchronized (lock(prefsNode)) {
jtulach@1263: //If removed, return silently
jtulach@1263: if (isRemoved(prefsNode))
jtulach@1263: return;
jtulach@1263:
jtulach@1263: // Import any preferences at this node
jtulach@1263: Element firstXmlKid = (Element) xmlKids.item(0);
jtulach@1263: ImportPrefs(prefsNode, firstXmlKid);
jtulach@1263: prefsKids = new Preferences[numXmlKids - 1];
jtulach@1263:
jtulach@1263: // Get involved children
jtulach@1263: for (int i=1; i < numXmlKids; i++) {
jtulach@1263: Element xmlKid = (Element) xmlKids.item(i);
jtulach@1263: prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name"));
jtulach@1263: }
jtulach@1263: } // unlocked the node
jtulach@1263: // import children
jtulach@1263: for (int i=1; i < numXmlKids; i++)
jtulach@1263: ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i));
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Import the preferences described by the specified XML element
jtulach@1263: * (a map from a preferences document) into the specified
jtulach@1263: * preferences node.
jtulach@1263: * /
jtulach@1263: private static void ImportPrefs(Preferences prefsNode, Element map) {
jtulach@1263: NodeList entries = map.getChildNodes();
jtulach@1263: for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) {
jtulach@1263: Element entry = (Element) entries.item(i);
jtulach@1263: prefsNode.put(entry.getAttribute("key"),
jtulach@1263: entry.getAttribute("value"));
jtulach@1263: }
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Export the specified Map to a map document on
jtulach@1263: * the specified OutputStream as per the prefs DTD. This is used
jtulach@1263: * as the internal (undocumented) format for FileSystemPrefs.
jtulach@1263: *
jtulach@1263: * @throws IOException if writing to the specified output stream
jtulach@1263: * results in an IOException.
jtulach@1263: */
jtulach@1263: public void exportMap(OutputStream os, Map map) throws IOException {
jtulach@1263: /*
jtulach@1263: Document doc = createPrefsDoc("map");
jtulach@1263: Element xmlMap = doc.getDocumentElement( ) ;
jtulach@1263: xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION);
jtulach@1263:
jtulach@1263: for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
jtulach@1263: Map.Entry e = (Map.Entry) i.next();
jtulach@1263: Element xe = (Element)
jtulach@1263: xmlMap.appendChild(doc.createElement("entry"));
jtulach@1263: xe.setAttribute("key", (String) e.getKey());
jtulach@1263: xe.setAttribute("value", (String) e.getValue());
jtulach@1263: }
jtulach@1263:
jtulach@1263: writeDoc(doc, os);
jtulach@1263: */
jtulach@1263: }
jtulach@1263:
jtulach@1263: /**
jtulach@1263: * Import Map from the specified input stream, which is assumed
jtulach@1263: * to contain a map document as per the prefs DTD. This is used
jtulach@1263: * as the internal (undocumented) format for FileSystemPrefs. The
jtulach@1263: * key-value pairs specified in the XML document will be put into
jtulach@1263: * the specified Map. (If this Map is empty, it will contain exactly
jtulach@1263: * the key-value pairs int the XML-document when this method returns.)
jtulach@1263: *
jtulach@1263: * @throws IOException if reading from the specified output stream
jtulach@1263: * results in an IOException.
jtulach@1263: * @throws InvalidPreferencesFormatException Data on input stream does not
jtulach@1263: * constitute a valid XML document with the mandated document type.
jtulach@1263: */
jtulach@1263: public void importMap(InputStream is, Map m)
jtulach@1263: throws IOException, InvalidPreferencesFormatException
jtulach@1263: {
jtulach@1263: /*
jtulach@1263: try {
jtulach@1263: Document doc = loadPrefsDoc(is);
jtulach@1263: Element xmlMap = doc.getDocumentElement();
jtulach@1263: // check version
jtulach@1263: String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION");
jtulach@1263: if (mapVersion.compareTo(MAP_XML_VERSION) > 0)
jtulach@1263: throw new InvalidPreferencesFormatException(
jtulach@1263: "Preferences map file format version " + mapVersion +
jtulach@1263: " is not supported. This java installation can read" +
jtulach@1263: " versions " + MAP_XML_VERSION + " or older. You may need" +
jtulach@1263: " to install a newer version of JDK.");
jtulach@1263:
jtulach@1263: NodeList entries = xmlMap.getChildNodes();
jtulach@1263: for (int i=0, numEntries=entries.getLength(); i