src/share/classes/sun/util/xml/DefaultPrefsXmlSupport.java
author Jaroslav Tulach <jtulach@netbeans.org>
Wed, 24 Jun 2009 17:29:29 +0200
branchxml-sax-and-dom-2
changeset 1263 24b6c30fbf71
child 1264 601d21ee9aa6
permissions -rw-r--r--
Simple output of Preferences is the same as the original one via DOM
jtulach@1263
     1
/*
jtulach@1263
     2
 * Copyright 2002-2006 Sun Microsystems, Inc.  All Rights Reserved.
jtulach@1263
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jtulach@1263
     4
 *
jtulach@1263
     5
 * This code is free software; you can redistribute it and/or modify it
jtulach@1263
     6
 * under the terms of the GNU General Public License version 2 only, as
jtulach@1263
     7
 * published by the Free Software Foundation.  Sun designates this
jtulach@1263
     8
 * particular file as subject to the "Classpath" exception as provided
jtulach@1263
     9
 * by Sun in the LICENSE file that accompanied this code.
jtulach@1263
    10
 *
jtulach@1263
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jtulach@1263
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jtulach@1263
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jtulach@1263
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jtulach@1263
    15
 * accompanied this code).
jtulach@1263
    16
 *
jtulach@1263
    17
 * You should have received a copy of the GNU General Public License version
jtulach@1263
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jtulach@1263
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jtulach@1263
    20
 *
jtulach@1263
    21
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
jtulach@1263
    22
 * CA 95054 USA or visit www.sun.com if you need additional information or
jtulach@1263
    23
 * have any questions.
jtulach@1263
    24
 */
jtulach@1263
    25
jtulach@1263
    26
package sun.util.xml;
jtulach@1263
    27
jtulach@1263
    28
import java.util.*;
jtulach@1263
    29
import java.util.prefs.*;
jtulach@1263
    30
import java.io.*;
jtulach@1263
    31
jtulach@1263
    32
/**
jtulach@1263
    33
 * Simplified XML Support for java.util.prefs. Methods to import and export preference
jtulach@1263
    34
 * nodes and subtrees.
jtulach@1263
    35
 *
jtulach@1263
    36
 * @author  Jaroslav Tulach
jtulach@1263
    37
 * @since   1.7
jtulach@1263
    38
 */
jtulach@1263
    39
public class DefaultPrefsXmlSupport extends sun.util.xml.PrefsXmlSupport {
jtulach@1263
    40
    public void export(OutputStream os, final Preferences p, boolean subTree)
jtulach@1263
    41
        throws IOException, BackingStoreException {
jtulach@1263
    42
        if (isRemoved(p))
jtulach@1263
    43
            throw new IllegalStateException("Node has been removed");
jtulach@1263
    44
        PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
jtulach@1263
    45
        w.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
jtulach@1263
    46
        w.println("<!DOCTYPE preferences SYSTEM \"http://java.sun.com/dtd/preferences.dtd\">");
jtulach@1263
    47
        w.println("<preferences EXTERNAL_XML_VERSION=\"1.0\">");
jtulach@1263
    48
        w.print("  <root type=\"");
jtulach@1263
    49
        w.print(p.isUserNode() ? "user" : "system");
jtulach@1263
    50
        w.println("\">");
jtulach@1263
    51
        LinkedList<Preferences> ancestors = new LinkedList<Preferences>();
jtulach@1263
    52
        for (Preferences kid = p, dad = kid.parent(); dad != null;
jtulach@1263
    53
                kid = dad, dad = kid.parent()) {
jtulach@1263
    54
            ancestors.addFirst(kid);
jtulach@1263
    55
        }
jtulach@1263
    56
        String indent = "  ";
jtulach@1263
    57
        for (Preferences pref : ancestors) {
jtulach@1263
    58
            indent = "  " + indent;
jtulach@1263
    59
            w.print(indent); w.println("<map/>");
jtulach@1263
    60
            w.print(indent); w.print("<node name=\""); w.print(pref.name()); w.println("\">");
jtulach@1263
    61
        }
jtulach@1263
    62
jtulach@1263
    63
        putPreferencesInXml(w, indent + "  ", p, subTree);
jtulach@1263
    64
        for (Preferences pref : ancestors) {
jtulach@1263
    65
            w.print(indent); w.println("</node>");
jtulach@1263
    66
            indent = indent.substring(2);
jtulach@1263
    67
        }
jtulach@1263
    68
jtulach@1263
    69
        w.println("  </root>");
jtulach@1263
    70
        w.println("</preferences>");
jtulach@1263
    71
        w.flush();
jtulach@1263
    72
    }
jtulach@1263
    73
jtulach@1263
    74
    private static void putPreferencesInXml(
jtulach@1263
    75
        PrintWriter w, String indent, Preferences prefs, boolean subTree
jtulach@1263
    76
    ) throws BackingStoreException {
jtulach@1263
    77
        Preferences[] kidsCopy = null;
jtulach@1263
    78
        String[] kidNames = null;
jtulach@1263
    79
jtulach@1263
    80
        // Node is locked to export its contents and get a
jtulach@1263
    81
        // copy of children, then lock is released,
jtulach@1263
    82
        // and, if subTree = true, recursive calls are made on children
jtulach@1263
    83
        synchronized (lock(prefs)) {
jtulach@1263
    84
            // Check if this node was concurrently removed. If yes
jtulach@1263
    85
            // don't print it
jtulach@1263
    86
            if (isRemoved(prefs)) {
jtulach@1263
    87
                return;
jtulach@1263
    88
            }
jtulach@1263
    89
            // Put map in xml element
jtulach@1263
    90
            String[] keys = prefs.keys();
jtulach@1263
    91
            if (keys.length == 0) {
jtulach@1263
    92
                w.print(indent); w.println("<map/>");
jtulach@1263
    93
            } else {
jtulach@1263
    94
                w.print(indent); w.println("<map>");
jtulach@1263
    95
                for (int i=0; i<keys.length; i++) {
jtulach@1263
    96
                    w.print(indent); w.print("  <entry key=\"");
jtulach@1263
    97
                    w.print(keys[i]);
jtulach@1263
    98
                    w.print("\" value=\"");
jtulach@1263
    99
                    w.print(prefs.get(keys[i], null));
jtulach@1263
   100
                    w.println("\"/>");
jtulach@1263
   101
                }
jtulach@1263
   102
                w.print(indent); w.println("</map>");
jtulach@1263
   103
            }
jtulach@1263
   104
            // Recurse if appropriate
jtulach@1263
   105
            if (subTree) {
jtulach@1263
   106
                /* Get a copy of kids while lock is held */
jtulach@1263
   107
                kidNames = prefs.childrenNames();
jtulach@1263
   108
                kidsCopy = new Preferences[kidNames.length];
jtulach@1263
   109
                for (int i = 0; i <  kidNames.length; i++)
jtulach@1263
   110
                    kidsCopy[i] = prefs.node(kidNames[i]);
jtulach@1263
   111
            }
jtulach@1263
   112
            // release lock
jtulach@1263
   113
        }
jtulach@1263
   114
jtulach@1263
   115
        if (subTree) {
jtulach@1263
   116
            for (int i=0; i < kidNames.length; i++) {
jtulach@1263
   117
                w.print(indent); w.print("<node name=\"");
jtulach@1263
   118
                w.print(kidNames[i]);
jtulach@1263
   119
                w.println("\">");
jtulach@1263
   120
                putPreferencesInXml(w, "  " + indent, kidsCopy[i], subTree);
jtulach@1263
   121
                w.print(indent); w.println("</node>");
jtulach@1263
   122
            }
jtulach@1263
   123
        }
jtulach@1263
   124
    }
jtulach@1263
   125
jtulach@1263
   126
    /**
jtulach@1263
   127
     * Import preferences from the specified input stream, which is assumed
jtulach@1263
   128
     * to contain an XML document in the format described in the Preferences
jtulach@1263
   129
     * spec.
jtulach@1263
   130
     *
jtulach@1263
   131
     * @throws IOException if reading from the specified output stream
jtulach@1263
   132
     *         results in an <tt>IOException</tt>.
jtulach@1263
   133
     * @throws InvalidPreferencesFormatException Data on input stream does not
jtulach@1263
   134
     *         constitute a valid XML document with the mandated document type.
jtulach@1263
   135
     */
jtulach@1263
   136
    public void importPreferences(InputStream is)
jtulach@1263
   137
        throws IOException, InvalidPreferencesFormatException
jtulach@1263
   138
    {
jtulach@1263
   139
        /*
jtulach@1263
   140
        try {
jtulach@1263
   141
            Document doc = loadPrefsDoc(is);
jtulach@1263
   142
            String xmlVersion =
jtulach@1263
   143
                doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION");
jtulach@1263
   144
            if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
jtulach@1263
   145
                throw new InvalidPreferencesFormatException(
jtulach@1263
   146
                "Exported preferences file format version " + xmlVersion +
jtulach@1263
   147
                " is not supported. This java installation can read" +
jtulach@1263
   148
                " versions " + EXTERNAL_XML_VERSION + " or older. You may need" +
jtulach@1263
   149
                " to install a newer version of JDK.");
jtulach@1263
   150
jtulach@1263
   151
            Element xmlRoot = (Element) doc.getDocumentElement().
jtulach@1263
   152
                                               getChildNodes().item(0);
jtulach@1263
   153
            Preferences prefsRoot =
jtulach@1263
   154
                (xmlRoot.getAttribute("type").equals("user") ?
jtulach@1263
   155
                            Preferences.userRoot() : Preferences.systemRoot());
jtulach@1263
   156
            ImportSubtree(prefsRoot, xmlRoot);
jtulach@1263
   157
        } catch(SAXException e) {
jtulach@1263
   158
            throw new InvalidPreferencesFormatException(e);
jtulach@1263
   159
        }
jtulach@1263
   160
         */
jtulach@1263
   161
    }
jtulach@1263
   162
jtulach@1263
   163
    /**
jtulach@1263
   164
     * Create a new prefs XML document.
jtulach@1263
   165
     *
jtulach@1263
   166
    private static Document createPrefsDoc( String qname ) {
jtulach@1263
   167
        try {
jtulach@1263
   168
            DOMImplementation di = DocumentBuilderFactory.newInstance().
jtulach@1263
   169
                newDocumentBuilder().getDOMImplementation();
jtulach@1263
   170
            DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI);
jtulach@1263
   171
            return di.createDocument(null, qname, dt);
jtulach@1263
   172
        } catch(ParserConfigurationException e) {
jtulach@1263
   173
            throw new AssertionError(e);
jtulach@1263
   174
        }
jtulach@1263
   175
    }
jtulach@1263
   176
jtulach@1263
   177
    /**
jtulach@1263
   178
     * Load an XML document from specified input stream, which must
jtulach@1263
   179
     * have the requisite DTD URI.
jtulach@1263
   180
     *
jtulach@1263
   181
    private static Document loadPrefsDoc(InputStream in)
jtulach@1263
   182
        throws SAXException, IOException
jtulach@1263
   183
    {
jtulach@1263
   184
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
jtulach@1263
   185
        dbf.setIgnoringElementContentWhitespace(true);
jtulach@1263
   186
        dbf.setValidating(true);
jtulach@1263
   187
        dbf.setCoalescing(true);
jtulach@1263
   188
        dbf.setIgnoringComments(true);
jtulach@1263
   189
        try {
jtulach@1263
   190
            DocumentBuilder db = dbf.newDocumentBuilder();
jtulach@1263
   191
            db.setEntityResolver(new Resolver());
jtulach@1263
   192
            db.setErrorHandler(new EH());
jtulach@1263
   193
            return db.parse(new InputSource(in));
jtulach@1263
   194
        } catch (ParserConfigurationException e) {
jtulach@1263
   195
            throw new AssertionError(e);
jtulach@1263
   196
        }
jtulach@1263
   197
    }
jtulach@1263
   198
jtulach@1263
   199
    /**
jtulach@1263
   200
     * Write XML document to the specified output stream.
jtulach@1263
   201
     *
jtulach@1263
   202
    private static final void writeDoc(Document doc, OutputStream out)
jtulach@1263
   203
        throws IOException
jtulach@1263
   204
    {
jtulach@1263
   205
        try {
jtulach@1263
   206
            TransformerFactory tf = TransformerFactory.newInstance();
jtulach@1263
   207
            try {
jtulach@1263
   208
                tf.setAttribute("indent-number", new Integer(2));
jtulach@1263
   209
            } catch (IllegalArgumentException iae) {
jtulach@1263
   210
                //Ignore the IAE. Should not fail the writeout even the
jtulach@1263
   211
                //transformer provider does not support "indent-number".
jtulach@1263
   212
            }
jtulach@1263
   213
            Transformer t = tf.newTransformer();
jtulach@1263
   214
            t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());
jtulach@1263
   215
            t.setOutputProperty(OutputKeys.INDENT, "yes");
jtulach@1263
   216
            //Transformer resets the "indent" info if the "result" is a StreamResult with
jtulach@1263
   217
            //an OutputStream object embedded, creating a Writer object on top of that
jtulach@1263
   218
            //OutputStream object however works.
jtulach@1263
   219
            t.transform(new DOMSource(doc),
jtulach@1263
   220
                        new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))));
jtulach@1263
   221
        } catch(TransformerException e) {
jtulach@1263
   222
            throw new AssertionError(e);
jtulach@1263
   223
        }
jtulach@1263
   224
    }
jtulach@1263
   225
jtulach@1263
   226
    /**
jtulach@1263
   227
     * Recursively traverse the specified preferences node and store
jtulach@1263
   228
     * the described preferences into the system or current user
jtulach@1263
   229
     * preferences tree, as appropriate.
jtulach@1263
   230
     *
jtulach@1263
   231
    private static void ImportSubtree(Preferences prefsNode, Element xmlNode) {
jtulach@1263
   232
        NodeList xmlKids = xmlNode.getChildNodes();
jtulach@1263
   233
        int numXmlKids = xmlKids.getLength();
jtulach@1263
   234
        /*
jtulach@1263
   235
         * We first lock the node, import its contents and get
jtulach@1263
   236
         * child nodes. Then we unlock the node and go to children
jtulach@1263
   237
         * Since some of the children might have been concurrently
jtulach@1263
   238
         * deleted we check for this.
jtulach@1263
   239
         * /
jtulach@1263
   240
        Preferences[] prefsKids;
jtulach@1263
   241
        /* Lock the node * /
jtulach@1263
   242
        synchronized (lock(prefsNode)) {
jtulach@1263
   243
            //If removed, return silently
jtulach@1263
   244
            if (isRemoved(prefsNode))
jtulach@1263
   245
                return;
jtulach@1263
   246
jtulach@1263
   247
            // Import any preferences at this node
jtulach@1263
   248
            Element firstXmlKid = (Element) xmlKids.item(0);
jtulach@1263
   249
            ImportPrefs(prefsNode, firstXmlKid);
jtulach@1263
   250
            prefsKids = new Preferences[numXmlKids - 1];
jtulach@1263
   251
jtulach@1263
   252
            // Get involved children
jtulach@1263
   253
            for (int i=1; i < numXmlKids; i++) {
jtulach@1263
   254
                Element xmlKid = (Element) xmlKids.item(i);
jtulach@1263
   255
                prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name"));
jtulach@1263
   256
            }
jtulach@1263
   257
        } // unlocked the node
jtulach@1263
   258
        // import children
jtulach@1263
   259
        for (int i=1; i < numXmlKids; i++)
jtulach@1263
   260
            ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i));
jtulach@1263
   261
    }
jtulach@1263
   262
jtulach@1263
   263
    /**
jtulach@1263
   264
     * Import the preferences described by the specified XML element
jtulach@1263
   265
     * (a map from a preferences document) into the specified
jtulach@1263
   266
     * preferences node.
jtulach@1263
   267
     * /
jtulach@1263
   268
    private static void ImportPrefs(Preferences prefsNode, Element map) {
jtulach@1263
   269
        NodeList entries = map.getChildNodes();
jtulach@1263
   270
        for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) {
jtulach@1263
   271
            Element entry = (Element) entries.item(i);
jtulach@1263
   272
            prefsNode.put(entry.getAttribute("key"),
jtulach@1263
   273
                          entry.getAttribute("value"));
jtulach@1263
   274
        }
jtulach@1263
   275
    }
jtulach@1263
   276
jtulach@1263
   277
    /**
jtulach@1263
   278
     * Export the specified Map<String,String> to a map document on
jtulach@1263
   279
     * the specified OutputStream as per the prefs DTD.  This is used
jtulach@1263
   280
     * as the internal (undocumented) format for FileSystemPrefs.
jtulach@1263
   281
     *
jtulach@1263
   282
     * @throws IOException if writing to the specified output stream
jtulach@1263
   283
     *         results in an <tt>IOException</tt>.
jtulach@1263
   284
     */
jtulach@1263
   285
    public void exportMap(OutputStream os, Map map) throws IOException {
jtulach@1263
   286
        /*
jtulach@1263
   287
        Document doc = createPrefsDoc("map");
jtulach@1263
   288
        Element xmlMap = doc.getDocumentElement( ) ;
jtulach@1263
   289
        xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION);
jtulach@1263
   290
jtulach@1263
   291
        for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
jtulach@1263
   292
            Map.Entry e = (Map.Entry) i.next();
jtulach@1263
   293
            Element xe = (Element)
jtulach@1263
   294
                xmlMap.appendChild(doc.createElement("entry"));
jtulach@1263
   295
            xe.setAttribute("key",   (String) e.getKey());
jtulach@1263
   296
            xe.setAttribute("value", (String) e.getValue());
jtulach@1263
   297
        }
jtulach@1263
   298
jtulach@1263
   299
        writeDoc(doc, os);
jtulach@1263
   300
         */
jtulach@1263
   301
    }
jtulach@1263
   302
jtulach@1263
   303
    /**
jtulach@1263
   304
     * Import Map from the specified input stream, which is assumed
jtulach@1263
   305
     * to contain a map document as per the prefs DTD.  This is used
jtulach@1263
   306
     * as the internal (undocumented) format for FileSystemPrefs.  The
jtulach@1263
   307
     * key-value pairs specified in the XML document will be put into
jtulach@1263
   308
     * the specified Map.  (If this Map is empty, it will contain exactly
jtulach@1263
   309
     * the key-value pairs int the XML-document when this method returns.)
jtulach@1263
   310
     *
jtulach@1263
   311
     * @throws IOException if reading from the specified output stream
jtulach@1263
   312
     *         results in an <tt>IOException</tt>.
jtulach@1263
   313
     * @throws InvalidPreferencesFormatException Data on input stream does not
jtulach@1263
   314
     *         constitute a valid XML document with the mandated document type.
jtulach@1263
   315
     */
jtulach@1263
   316
    public void importMap(InputStream is, Map m)
jtulach@1263
   317
        throws IOException, InvalidPreferencesFormatException
jtulach@1263
   318
    {
jtulach@1263
   319
        /*
jtulach@1263
   320
        try {
jtulach@1263
   321
            Document doc = loadPrefsDoc(is);
jtulach@1263
   322
            Element xmlMap = doc.getDocumentElement();
jtulach@1263
   323
            // check version
jtulach@1263
   324
            String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION");
jtulach@1263
   325
            if (mapVersion.compareTo(MAP_XML_VERSION) > 0)
jtulach@1263
   326
                throw new InvalidPreferencesFormatException(
jtulach@1263
   327
                "Preferences map file format version " + mapVersion +
jtulach@1263
   328
                " is not supported. This java installation can read" +
jtulach@1263
   329
                " versions " + MAP_XML_VERSION + " or older. You may need" +
jtulach@1263
   330
                " to install a newer version of JDK.");
jtulach@1263
   331
jtulach@1263
   332
            NodeList entries = xmlMap.getChildNodes();
jtulach@1263
   333
            for (int i=0, numEntries=entries.getLength(); i<numEntries; i++) {
jtulach@1263
   334
                Element entry = (Element) entries.item(i);
jtulach@1263
   335
                m.put(entry.getAttribute("key"), entry.getAttribute("value"));
jtulach@1263
   336
            }
jtulach@1263
   337
        } catch(SAXException e) {
jtulach@1263
   338
            throw new InvalidPreferencesFormatException(e);
jtulach@1263
   339
        }
jtulach@1263
   340
         */
jtulach@1263
   341
    }
jtulach@1263
   342
/*
jtulach@1263
   343
    private static class Resolver implements EntityResolver {
jtulach@1263
   344
        public InputSource resolveEntity(String pid, String sid)
jtulach@1263
   345
            throws SAXException
jtulach@1263
   346
        {
jtulach@1263
   347
            if (sid.equals(PREFS_DTD_URI)) {
jtulach@1263
   348
                InputSource is;
jtulach@1263
   349
                is = new InputSource(new StringReader(PREFS_DTD));
jtulach@1263
   350
                is.setSystemId(PREFS_DTD_URI);
jtulach@1263
   351
                return is;
jtulach@1263
   352
            }
jtulach@1263
   353
            throw new SAXException("Invalid system identifier: " + sid);
jtulach@1263
   354
        }
jtulach@1263
   355
    }
jtulach@1263
   356
jtulach@1263
   357
    private static class EH implements ErrorHandler {
jtulach@1263
   358
        public void error(SAXParseException x) throws SAXException {
jtulach@1263
   359
            throw x;
jtulach@1263
   360
        }
jtulach@1263
   361
        public void fatalError(SAXParseException x) throws SAXException {
jtulach@1263
   362
            throw x;
jtulach@1263
   363
        }
jtulach@1263
   364
        public void warning(SAXParseException x) throws SAXException {
jtulach@1263
   365
            throw x;
jtulach@1263
   366
        }
jtulach@1263
   367
    }
jtulach@1263
   368
*/
jtulach@1263
   369
    private static boolean isRemoved(Preferences p) {
jtulach@1263
   370
        try {
jtulach@1263
   371
            p.parent(); // throws IllegalStateException if removed;
jtulach@1263
   372
            return false;
jtulach@1263
   373
        } catch (IllegalStateException ex) {
jtulach@1263
   374
            return true;
jtulach@1263
   375
        }
jtulach@1263
   376
    }
jtulach@1263
   377
jtulach@1263
   378
    private static Object lock(Preferences p) {
jtulach@1263
   379
        /** JST-XXX: Needs reflection or accessor:
jtulach@1263
   380
         * http://wiki.apidesign.org/wiki/FriendPackages
jtulach@1263
   381
        return ((AbstractPreferences)prefs).lock;
jtulach@1263
   382
         */
jtulach@1263
   383
        return p;
jtulach@1263
   384
    }
jtulach@1263
   385
}