KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > prefs > XmlSupport


1 /*
2  * @(#)XmlSupport.java 1.7 04/01/12
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.prefs;
9
10 import java.util.*;
11 import java.io.*;
12 import javax.xml.parsers.*;
13 import javax.xml.transform.*;
14 import javax.xml.transform.dom.*;
15 import javax.xml.transform.stream.*;
16 import org.xml.sax.*;
17 import org.w3c.dom.*;
18
19 /**
20  * XML Support for java.util.prefs. Methods to import and export preference
21  * nodes and subtrees.
22  *
23  * @author Josh Bloch and Mark Reinhold
24  * @version 1.7, 01/12/04
25  * @see Preferences
26  * @since 1.4
27  */

28 class XmlSupport {
29     // The required DTD URI for exported preferences
30
private static final String JavaDoc PREFS_DTD_URI =
31         "http://java.sun.com/dtd/preferences.dtd";
32
33     // The actual DTD corresponding to the URI
34
private static final String JavaDoc PREFS_DTD =
35         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
36
37         "<!-- DTD for preferences -->" +
38
39         "<!ELEMENT preferences (root) >" +
40         "<!ATTLIST preferences" +
41         " EXTERNAL_XML_VERSION CDATA \"0.0\" >" +
42
43         "<!ELEMENT root (map, node*) >" +
44         "<!ATTLIST root" +
45         " type (system|user) #REQUIRED >" +
46
47         "<!ELEMENT node (map, node*) >" +
48         "<!ATTLIST node" +
49         " name CDATA #REQUIRED >" +
50
51         "<!ELEMENT map (entry*) >" +
52         "<!ATTLIST map" +
53         " MAP_XML_VERSION CDATA \"0.0\" >" +
54         "<!ELEMENT entry EMPTY >" +
55         "<!ATTLIST entry" +
56         " key CDATA #REQUIRED" +
57         " value CDATA #REQUIRED >" ;
58     /**
59      * Version number for the format exported preferences files.
60      */

61     private static final String JavaDoc EXTERNAL_XML_VERSION = "1.0";
62     
63     /*
64      * Version number for the internal map files.
65      */

66     private static final String JavaDoc MAP_XML_VERSION = "1.0";
67     
68     /**
69      * Export the specified preferences node and, if subTree is true, all
70      * subnodes, to the specified output stream. Preferences are exported as
71      * an XML document conforming to the definition in the Preferences spec.
72      *
73      * @throws IOException if writing to the specified output stream
74      * results in an <tt>IOException</tt>.
75      * @throws BackingStoreException if preference data cannot be read from
76      * backing store.
77      * @throws IllegalStateException if this node (or an ancestor) has been
78      * removed with the {@link #removeNode()} method.
79      */

80     static void export(OutputStream os, final Preferences JavaDoc p, boolean subTree)
81         throws IOException, BackingStoreException JavaDoc {
82         if (((AbstractPreferences JavaDoc)p).isRemoved())
83             throw new IllegalStateException JavaDoc("Node has been removed");
84         Document doc = createPrefsDoc("preferences");
85         Element preferences = doc.getDocumentElement() ;
86         preferences.setAttribute("EXTERNAL_XML_VERSION", EXTERNAL_XML_VERSION);
87         Element xmlRoot = (Element)
88         preferences.appendChild(doc.createElement("root"));
89         xmlRoot.setAttribute("type", (p.isUserNode() ? "user" : "system"));
90
91         // Get bottom-up list of nodes from p to root, excluding root
92
List ancestors = new ArrayList();
93             
94         for (Preferences JavaDoc kid = p, dad = kid.parent(); dad != null;
95                                    kid = dad, dad = kid.parent()) {
96             ancestors.add(kid);
97         }
98         Element e = xmlRoot;
99         for (int i=ancestors.size()-1; i >= 0; i--) {
100             e.appendChild(doc.createElement("map"));
101             e = (Element) e.appendChild(doc.createElement("node"));
102             e.setAttribute("name", ((Preferences JavaDoc)ancestors.get(i)).name());
103         }
104         putPreferencesInXml(e, doc, p, subTree);
105
106         writeDoc(doc, os);
107     }
108
109     /**
110      * Put the preferences in the specified Preferences node into the
111      * specified XML element which is assumed to represent a node
112      * in the specified XML document which is assumed to conform to
113      * PREFS_DTD. If subTree is true, create children of the specified
114      * XML node conforming to all of the children of the specified
115      * Preferences node and recurse.
116      *
117      * @throws BackingStoreException if it is not possible to read
118      * the preferences or children out of the specified
119      * preferences node.
120      */

121     private static void putPreferencesInXml(Element elt, Document doc,
122                Preferences JavaDoc prefs, boolean subTree) throws BackingStoreException JavaDoc
123     {
124         Preferences JavaDoc[] kidsCopy = null;
125         String JavaDoc[] kidNames = null;
126         
127         // Node is locked to export its contents and get a
128
// copy of children, then lock is released,
129
// and, if subTree = true, recursive calls are made on children
130
synchronized (((AbstractPreferences JavaDoc)prefs).lock) {
131             // Check if this node was concurrently removed. If yes
132
// remove it from XML Document and return.
133
if (((AbstractPreferences JavaDoc)prefs).isRemoved()) {
134                 elt.getParentNode().removeChild(elt);
135                 return;
136             }
137             // Put map in xml element
138
String JavaDoc[] keys = prefs.keys();
139             Element map = (Element) elt.appendChild(doc.createElement("map"));
140             for (int i=0; i<keys.length; i++) {
141                 Element entry = (Element)
142                     map.appendChild(doc.createElement("entry"));
143                 entry.setAttribute("key", keys[i]);
144                 // NEXT STATEMENT THROWS NULL PTR EXC INSTEAD OF ASSERT FAIL
145
entry.setAttribute("value", prefs.get(keys[i], null));
146             }
147             // Recurse if appropriate
148
if (subTree) {
149                 /* Get a copy of kids while lock is held */
150                 kidNames = prefs.childrenNames();
151                 kidsCopy = new Preferences JavaDoc[kidNames.length];
152                 for (int i = 0; i < kidNames.length; i++)
153                     kidsCopy[i] = prefs.node(kidNames[i]);
154             }
155             // release lock
156
}
157         
158         if (subTree) {
159             for (int i=0; i < kidNames.length; i++) {
160                 Element xmlKid = (Element)
161                     elt.appendChild(doc.createElement("node"));
162                 xmlKid.setAttribute("name", kidNames[i]);
163                 putPreferencesInXml(xmlKid, doc, kidsCopy[i], subTree);
164             }
165         }
166     }
167
168     /**
169      * Import preferences from the specified input stream, which is assumed
170      * to contain an XML document in the format described in the Preferences
171      * spec.
172      *
173      * @throws IOException if reading from the specified output stream
174      * results in an <tt>IOException</tt>.
175      * @throws InvalidPreferencesFormatException Data on input stream does not
176      * constitute a valid XML document with the mandated document type.
177      */

178     static void importPreferences(InputStream is)
179         throws IOException, InvalidPreferencesFormatException JavaDoc
180     {
181         try {
182             Document doc = loadPrefsDoc(is);
183             String JavaDoc xmlVersion =
184             ((Element)doc.getChildNodes().item(1)).getAttribute("EXTERNAL_XML_VERSION");
185             if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
186                 throw new InvalidPreferencesFormatException JavaDoc(
187                 "Exported preferences file format version " + xmlVersion +
188                 " is not supported. This java installation can read" +
189                 " versions " + EXTERNAL_XML_VERSION + " or older. You may need" +
190                 " to install a newer version of JDK.");
191             
192             Element xmlRoot = (Element) doc.getChildNodes().item(1).
193                                                getChildNodes().item(0);
194             Preferences JavaDoc prefsRoot =
195                 (xmlRoot.getAttribute("type").equals("user") ?
196                             Preferences.userRoot() : Preferences.systemRoot());
197             ImportSubtree(prefsRoot, xmlRoot);
198         } catch(SAXException e) {
199             throw new InvalidPreferencesFormatException JavaDoc(e);
200         }
201     }
202
203     /**
204      * Create a new prefs XML document.
205      */

206     private static Document createPrefsDoc( String JavaDoc qname ) {
207         try {
208             DOMImplementation di = DocumentBuilderFactory.newInstance().
209                 newDocumentBuilder().getDOMImplementation();
210             DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI);
211             return di.createDocument(null, qname, dt);
212         } catch(ParserConfigurationException e) {
213             throw new AssertionError JavaDoc(e);
214         }
215     }
216
217     /**
218      * Load an XML document from specified input stream, which must
219      * have the requisite DTD URI.
220      */

221     private static Document loadPrefsDoc(InputStream in)
222         throws SAXException, IOException
223     {
224         Resolver r = new Resolver();
225         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
226         dbf.setIgnoringElementContentWhitespace(true);
227         dbf.setValidating(true);
228         dbf.setCoalescing(true);
229         dbf.setIgnoringComments(true);
230         try {
231             DocumentBuilder db = dbf.newDocumentBuilder();
232             db.setEntityResolver(new Resolver());
233             db.setErrorHandler(new EH());
234             return db.parse(new InputSource(in));
235         } catch (ParserConfigurationException e) {
236             throw new AssertionError JavaDoc(e);
237         }
238     }
239
240     /**
241      * Write XML document to the specified output stream.
242      */

243     private static final void writeDoc(Document doc, OutputStream out)
244         throws IOException
245     {
246         try {
247             Transformer t = TransformerFactory.newInstance().newTransformer();
248             t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());
249             t.transform(new DOMSource(doc), new StreamResult(out));
250         } catch(TransformerException e) {
251             throw new AssertionError JavaDoc(e);
252         }
253     }
254
255     /**
256      * Recursively traverse the specified preferences node and store
257      * the described preferences into the system or current user
258      * preferences tree, as appropriate.
259      */

260     private static void ImportSubtree(Preferences JavaDoc prefsNode, Element xmlNode) {
261         NodeList xmlKids = xmlNode.getChildNodes();
262         int numXmlKids = xmlKids.getLength();
263         /*
264          * We first lock the node, import its contents and get
265          * child nodes. Then we unlock the node and go to children
266          * Since some of the children might have been concurrently
267          * deleted we check for this.
268          */

269         Preferences JavaDoc[] prefsKids;
270         /* Lock the node */
271         synchronized (((AbstractPreferences JavaDoc)prefsNode).lock) {
272             //If removed, return silently
273
if (((AbstractPreferences JavaDoc)prefsNode).isRemoved())
274                 return;
275
276             // Import any preferences at this node
277
Element firstXmlKid = (Element) xmlKids.item(0);
278             ImportPrefs(prefsNode, firstXmlKid);
279             prefsKids = new Preferences JavaDoc[numXmlKids - 1];
280
281             // Get involved children
282
for (int i=1; i < numXmlKids; i++) {
283                 Element xmlKid = (Element) xmlKids.item(i);
284                 prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name"));
285             }
286         } // unlocked the node
287
// import children
288
for (int i=1; i < numXmlKids; i++)
289             ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i));
290     }
291
292     /**
293      * Import the preferences described by the specified XML element
294      * (a map from a preferences document) into the specified
295      * preferences node.
296      */

297     private static void ImportPrefs(Preferences JavaDoc prefsNode, Element map) {
298         NodeList entries = map.getChildNodes();
299         for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) {
300             Element entry = (Element) entries.item(i);
301             prefsNode.put(entry.getAttribute("key"),
302                           entry.getAttribute("value"));
303         }
304     }
305
306     /**
307      * Export the specified Map<String,String> to a map document on
308      * the specified OutputStream as per the prefs DTD. This is used
309      * as the internal (undocumented) format for FileSystemPrefs.
310      *
311      * @throws IOException if writing to the specified output stream
312      * results in an <tt>IOException</tt>.
313      */

314     static void exportMap(OutputStream os, Map map) throws IOException {
315         Document doc = createPrefsDoc("map");
316         Element xmlMap = doc.getDocumentElement( ) ;
317         xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION);
318
319         for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
320             Map.Entry e = (Map.Entry) i.next();
321             Element xe = (Element)
322                 xmlMap.appendChild(doc.createElement("entry"));
323             xe.setAttribute("key", (String JavaDoc) e.getKey());
324             xe.setAttribute("value", (String JavaDoc) e.getValue());
325         }
326
327         writeDoc(doc, os);
328     }
329
330     /**
331      * Import Map from the specified input stream, which is assumed
332      * to contain a map document as per the prefs DTD. This is used
333      * as the internal (undocumented) format for FileSystemPrefs. The
334      * key-value pairs specified in the XML document will be put into
335      * the specified Map. (If this Map is empty, it will contain exactly
336      * the key-value pairs int the XML-document when this method returns.)
337      *
338      * @throws IOException if reading from the specified output stream
339      * results in an <tt>IOException</tt>.
340      * @throws InvalidPreferencesFormatException Data on input stream does not
341      * constitute a valid XML document with the mandated document type.
342      */

343     static void importMap(InputStream is, Map m)
344         throws IOException, InvalidPreferencesFormatException JavaDoc
345     {
346         try {
347             Document doc = loadPrefsDoc(is);
348             Element xmlMap = (Element) doc.getChildNodes().item(1);
349             // check version
350
String JavaDoc mapVersion = xmlMap.getAttribute("MAP_XML_VERSION");
351             if (mapVersion.compareTo(MAP_XML_VERSION) > 0)
352                 throw new InvalidPreferencesFormatException JavaDoc(
353                 "Preferences map file format version " + mapVersion +
354                 " is not supported. This java installation can read" +
355                 " versions " + MAP_XML_VERSION + " or older. You may need" +
356                 " to install a newer version of JDK.");
357                 
358             NodeList entries = xmlMap.getChildNodes();
359             for (int i=0, numEntries=entries.getLength(); i<numEntries; i++) {
360                 Element entry = (Element) entries.item(i);
361                 m.put(entry.getAttribute("key"), entry.getAttribute("value"));
362             }
363         } catch(SAXException e) {
364             throw new InvalidPreferencesFormatException JavaDoc(e);
365         }
366     }
367
368     private static class Resolver implements EntityResolver {
369         public InputSource resolveEntity(String JavaDoc pid, String JavaDoc sid)
370             throws SAXException
371         {
372             if (sid.equals(PREFS_DTD_URI)) {
373                 InputSource is;
374                 is = new InputSource(new StringReader(PREFS_DTD));
375                 is.setSystemId(PREFS_DTD_URI);
376                 return is;
377             }
378             throw new SAXException("Invalid system identifier: " + sid);
379         }
380     }
381
382     private static class EH implements ErrorHandler {
383         public void error(SAXParseException x) throws SAXException {
384             throw x;
385         }
386         public void fatalError(SAXParseException x) throws SAXException {
387             throw x;
388         }
389         public void warning(SAXParseException x) throws SAXException {
390             throw x;
391         }
392     }
393 }
394
Popular Tags