1.1 --- a/build.xml Wed Jun 24 16:38:24 2009 +0200
1.2 +++ b/build.xml Wed Jun 24 17:29:29 2009 +0200
1.3 @@ -373,6 +373,12 @@
1.4 >
1.5 <bootclasspath refid="boot"/>
1.6 </javac>
1.7 + <javac srcdir="test/java/util/prefs/Preferences" destdir="${test.dir}"
1.8 + includeantruntime="false" includejavaruntime="false"
1.9 + debug="true" debuglevel="lines,vars,source"
1.10 + >
1.11 + <bootclasspath refid="boot"/>
1.12 + </javac>
1.13
1.14 <java classname="XMLReadAndWriteTest" fork="true">
1.15 <jvmarg value="-ea"/>
1.16 @@ -381,6 +387,13 @@
1.17 <pathelement location="${test.dir}"/>
1.18 </classpath>
1.19 </java>
1.20 + <java classname="XMLPreferencesTest" fork="true">
1.21 + <jvmarg value="-ea"/>
1.22 + <classpath>
1.23 + <path refid="boot"/>
1.24 + <pathelement location="${test.dir}"/>
1.25 + </classpath>
1.26 + </java>
1.27 </target>
1.28
1.29 <!-- shared routine to compile one of the modules -->
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/src/share/classes/sun/util/xml/DefaultPrefsXmlSupport.java Wed Jun 24 17:29:29 2009 +0200
2.3 @@ -0,0 +1,385 @@
2.4 +/*
2.5 + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2.7 + *
2.8 + * This code is free software; you can redistribute it and/or modify it
2.9 + * under the terms of the GNU General Public License version 2 only, as
2.10 + * published by the Free Software Foundation. Sun designates this
2.11 + * particular file as subject to the "Classpath" exception as provided
2.12 + * by Sun in the LICENSE file that accompanied this code.
2.13 + *
2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2.17 + * version 2 for more details (a copy is included in the LICENSE file that
2.18 + * accompanied this code).
2.19 + *
2.20 + * You should have received a copy of the GNU General Public License version
2.21 + * 2 along with this work; if not, write to the Free Software Foundation,
2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2.23 + *
2.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
2.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
2.26 + * have any questions.
2.27 + */
2.28 +
2.29 +package sun.util.xml;
2.30 +
2.31 +import java.util.*;
2.32 +import java.util.prefs.*;
2.33 +import java.io.*;
2.34 +
2.35 +/**
2.36 + * Simplified XML Support for java.util.prefs. Methods to import and export preference
2.37 + * nodes and subtrees.
2.38 + *
2.39 + * @author Jaroslav Tulach
2.40 + * @since 1.7
2.41 + */
2.42 +public class DefaultPrefsXmlSupport extends sun.util.xml.PrefsXmlSupport {
2.43 + public void export(OutputStream os, final Preferences p, boolean subTree)
2.44 + throws IOException, BackingStoreException {
2.45 + if (isRemoved(p))
2.46 + throw new IllegalStateException("Node has been removed");
2.47 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
2.48 + w.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
2.49 + w.println("<!DOCTYPE preferences SYSTEM \"http://java.sun.com/dtd/preferences.dtd\">");
2.50 + w.println("<preferences EXTERNAL_XML_VERSION=\"1.0\">");
2.51 + w.print(" <root type=\"");
2.52 + w.print(p.isUserNode() ? "user" : "system");
2.53 + w.println("\">");
2.54 + LinkedList<Preferences> ancestors = new LinkedList<Preferences>();
2.55 + for (Preferences kid = p, dad = kid.parent(); dad != null;
2.56 + kid = dad, dad = kid.parent()) {
2.57 + ancestors.addFirst(kid);
2.58 + }
2.59 + String indent = " ";
2.60 + for (Preferences pref : ancestors) {
2.61 + indent = " " + indent;
2.62 + w.print(indent); w.println("<map/>");
2.63 + w.print(indent); w.print("<node name=\""); w.print(pref.name()); w.println("\">");
2.64 + }
2.65 +
2.66 + putPreferencesInXml(w, indent + " ", p, subTree);
2.67 + for (Preferences pref : ancestors) {
2.68 + w.print(indent); w.println("</node>");
2.69 + indent = indent.substring(2);
2.70 + }
2.71 +
2.72 + w.println(" </root>");
2.73 + w.println("</preferences>");
2.74 + w.flush();
2.75 + }
2.76 +
2.77 + private static void putPreferencesInXml(
2.78 + PrintWriter w, String indent, Preferences prefs, boolean subTree
2.79 + ) throws BackingStoreException {
2.80 + Preferences[] kidsCopy = null;
2.81 + String[] kidNames = null;
2.82 +
2.83 + // Node is locked to export its contents and get a
2.84 + // copy of children, then lock is released,
2.85 + // and, if subTree = true, recursive calls are made on children
2.86 + synchronized (lock(prefs)) {
2.87 + // Check if this node was concurrently removed. If yes
2.88 + // don't print it
2.89 + if (isRemoved(prefs)) {
2.90 + return;
2.91 + }
2.92 + // Put map in xml element
2.93 + String[] keys = prefs.keys();
2.94 + if (keys.length == 0) {
2.95 + w.print(indent); w.println("<map/>");
2.96 + } else {
2.97 + w.print(indent); w.println("<map>");
2.98 + for (int i=0; i<keys.length; i++) {
2.99 + w.print(indent); w.print(" <entry key=\"");
2.100 + w.print(keys[i]);
2.101 + w.print("\" value=\"");
2.102 + w.print(prefs.get(keys[i], null));
2.103 + w.println("\"/>");
2.104 + }
2.105 + w.print(indent); w.println("</map>");
2.106 + }
2.107 + // Recurse if appropriate
2.108 + if (subTree) {
2.109 + /* Get a copy of kids while lock is held */
2.110 + kidNames = prefs.childrenNames();
2.111 + kidsCopy = new Preferences[kidNames.length];
2.112 + for (int i = 0; i < kidNames.length; i++)
2.113 + kidsCopy[i] = prefs.node(kidNames[i]);
2.114 + }
2.115 + // release lock
2.116 + }
2.117 +
2.118 + if (subTree) {
2.119 + for (int i=0; i < kidNames.length; i++) {
2.120 + w.print(indent); w.print("<node name=\"");
2.121 + w.print(kidNames[i]);
2.122 + w.println("\">");
2.123 + putPreferencesInXml(w, " " + indent, kidsCopy[i], subTree);
2.124 + w.print(indent); w.println("</node>");
2.125 + }
2.126 + }
2.127 + }
2.128 +
2.129 + /**
2.130 + * Import preferences from the specified input stream, which is assumed
2.131 + * to contain an XML document in the format described in the Preferences
2.132 + * spec.
2.133 + *
2.134 + * @throws IOException if reading from the specified output stream
2.135 + * results in an <tt>IOException</tt>.
2.136 + * @throws InvalidPreferencesFormatException Data on input stream does not
2.137 + * constitute a valid XML document with the mandated document type.
2.138 + */
2.139 + public void importPreferences(InputStream is)
2.140 + throws IOException, InvalidPreferencesFormatException
2.141 + {
2.142 + /*
2.143 + try {
2.144 + Document doc = loadPrefsDoc(is);
2.145 + String xmlVersion =
2.146 + doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION");
2.147 + if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
2.148 + throw new InvalidPreferencesFormatException(
2.149 + "Exported preferences file format version " + xmlVersion +
2.150 + " is not supported. This java installation can read" +
2.151 + " versions " + EXTERNAL_XML_VERSION + " or older. You may need" +
2.152 + " to install a newer version of JDK.");
2.153 +
2.154 + Element xmlRoot = (Element) doc.getDocumentElement().
2.155 + getChildNodes().item(0);
2.156 + Preferences prefsRoot =
2.157 + (xmlRoot.getAttribute("type").equals("user") ?
2.158 + Preferences.userRoot() : Preferences.systemRoot());
2.159 + ImportSubtree(prefsRoot, xmlRoot);
2.160 + } catch(SAXException e) {
2.161 + throw new InvalidPreferencesFormatException(e);
2.162 + }
2.163 + */
2.164 + }
2.165 +
2.166 + /**
2.167 + * Create a new prefs XML document.
2.168 + *
2.169 + private static Document createPrefsDoc( String qname ) {
2.170 + try {
2.171 + DOMImplementation di = DocumentBuilderFactory.newInstance().
2.172 + newDocumentBuilder().getDOMImplementation();
2.173 + DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI);
2.174 + return di.createDocument(null, qname, dt);
2.175 + } catch(ParserConfigurationException e) {
2.176 + throw new AssertionError(e);
2.177 + }
2.178 + }
2.179 +
2.180 + /**
2.181 + * Load an XML document from specified input stream, which must
2.182 + * have the requisite DTD URI.
2.183 + *
2.184 + private static Document loadPrefsDoc(InputStream in)
2.185 + throws SAXException, IOException
2.186 + {
2.187 + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2.188 + dbf.setIgnoringElementContentWhitespace(true);
2.189 + dbf.setValidating(true);
2.190 + dbf.setCoalescing(true);
2.191 + dbf.setIgnoringComments(true);
2.192 + try {
2.193 + DocumentBuilder db = dbf.newDocumentBuilder();
2.194 + db.setEntityResolver(new Resolver());
2.195 + db.setErrorHandler(new EH());
2.196 + return db.parse(new InputSource(in));
2.197 + } catch (ParserConfigurationException e) {
2.198 + throw new AssertionError(e);
2.199 + }
2.200 + }
2.201 +
2.202 + /**
2.203 + * Write XML document to the specified output stream.
2.204 + *
2.205 + private static final void writeDoc(Document doc, OutputStream out)
2.206 + throws IOException
2.207 + {
2.208 + try {
2.209 + TransformerFactory tf = TransformerFactory.newInstance();
2.210 + try {
2.211 + tf.setAttribute("indent-number", new Integer(2));
2.212 + } catch (IllegalArgumentException iae) {
2.213 + //Ignore the IAE. Should not fail the writeout even the
2.214 + //transformer provider does not support "indent-number".
2.215 + }
2.216 + Transformer t = tf.newTransformer();
2.217 + t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());
2.218 + t.setOutputProperty(OutputKeys.INDENT, "yes");
2.219 + //Transformer resets the "indent" info if the "result" is a StreamResult with
2.220 + //an OutputStream object embedded, creating a Writer object on top of that
2.221 + //OutputStream object however works.
2.222 + t.transform(new DOMSource(doc),
2.223 + new StreamResult(new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))));
2.224 + } catch(TransformerException e) {
2.225 + throw new AssertionError(e);
2.226 + }
2.227 + }
2.228 +
2.229 + /**
2.230 + * Recursively traverse the specified preferences node and store
2.231 + * the described preferences into the system or current user
2.232 + * preferences tree, as appropriate.
2.233 + *
2.234 + private static void ImportSubtree(Preferences prefsNode, Element xmlNode) {
2.235 + NodeList xmlKids = xmlNode.getChildNodes();
2.236 + int numXmlKids = xmlKids.getLength();
2.237 + /*
2.238 + * We first lock the node, import its contents and get
2.239 + * child nodes. Then we unlock the node and go to children
2.240 + * Since some of the children might have been concurrently
2.241 + * deleted we check for this.
2.242 + * /
2.243 + Preferences[] prefsKids;
2.244 + /* Lock the node * /
2.245 + synchronized (lock(prefsNode)) {
2.246 + //If removed, return silently
2.247 + if (isRemoved(prefsNode))
2.248 + return;
2.249 +
2.250 + // Import any preferences at this node
2.251 + Element firstXmlKid = (Element) xmlKids.item(0);
2.252 + ImportPrefs(prefsNode, firstXmlKid);
2.253 + prefsKids = new Preferences[numXmlKids - 1];
2.254 +
2.255 + // Get involved children
2.256 + for (int i=1; i < numXmlKids; i++) {
2.257 + Element xmlKid = (Element) xmlKids.item(i);
2.258 + prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name"));
2.259 + }
2.260 + } // unlocked the node
2.261 + // import children
2.262 + for (int i=1; i < numXmlKids; i++)
2.263 + ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i));
2.264 + }
2.265 +
2.266 + /**
2.267 + * Import the preferences described by the specified XML element
2.268 + * (a map from a preferences document) into the specified
2.269 + * preferences node.
2.270 + * /
2.271 + private static void ImportPrefs(Preferences prefsNode, Element map) {
2.272 + NodeList entries = map.getChildNodes();
2.273 + for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) {
2.274 + Element entry = (Element) entries.item(i);
2.275 + prefsNode.put(entry.getAttribute("key"),
2.276 + entry.getAttribute("value"));
2.277 + }
2.278 + }
2.279 +
2.280 + /**
2.281 + * Export the specified Map<String,String> to a map document on
2.282 + * the specified OutputStream as per the prefs DTD. This is used
2.283 + * as the internal (undocumented) format for FileSystemPrefs.
2.284 + *
2.285 + * @throws IOException if writing to the specified output stream
2.286 + * results in an <tt>IOException</tt>.
2.287 + */
2.288 + public void exportMap(OutputStream os, Map map) throws IOException {
2.289 + /*
2.290 + Document doc = createPrefsDoc("map");
2.291 + Element xmlMap = doc.getDocumentElement( ) ;
2.292 + xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION);
2.293 +
2.294 + for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
2.295 + Map.Entry e = (Map.Entry) i.next();
2.296 + Element xe = (Element)
2.297 + xmlMap.appendChild(doc.createElement("entry"));
2.298 + xe.setAttribute("key", (String) e.getKey());
2.299 + xe.setAttribute("value", (String) e.getValue());
2.300 + }
2.301 +
2.302 + writeDoc(doc, os);
2.303 + */
2.304 + }
2.305 +
2.306 + /**
2.307 + * Import Map from the specified input stream, which is assumed
2.308 + * to contain a map document as per the prefs DTD. This is used
2.309 + * as the internal (undocumented) format for FileSystemPrefs. The
2.310 + * key-value pairs specified in the XML document will be put into
2.311 + * the specified Map. (If this Map is empty, it will contain exactly
2.312 + * the key-value pairs int the XML-document when this method returns.)
2.313 + *
2.314 + * @throws IOException if reading from the specified output stream
2.315 + * results in an <tt>IOException</tt>.
2.316 + * @throws InvalidPreferencesFormatException Data on input stream does not
2.317 + * constitute a valid XML document with the mandated document type.
2.318 + */
2.319 + public void importMap(InputStream is, Map m)
2.320 + throws IOException, InvalidPreferencesFormatException
2.321 + {
2.322 + /*
2.323 + try {
2.324 + Document doc = loadPrefsDoc(is);
2.325 + Element xmlMap = doc.getDocumentElement();
2.326 + // check version
2.327 + String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION");
2.328 + if (mapVersion.compareTo(MAP_XML_VERSION) > 0)
2.329 + throw new InvalidPreferencesFormatException(
2.330 + "Preferences map file format version " + mapVersion +
2.331 + " is not supported. This java installation can read" +
2.332 + " versions " + MAP_XML_VERSION + " or older. You may need" +
2.333 + " to install a newer version of JDK.");
2.334 +
2.335 + NodeList entries = xmlMap.getChildNodes();
2.336 + for (int i=0, numEntries=entries.getLength(); i<numEntries; i++) {
2.337 + Element entry = (Element) entries.item(i);
2.338 + m.put(entry.getAttribute("key"), entry.getAttribute("value"));
2.339 + }
2.340 + } catch(SAXException e) {
2.341 + throw new InvalidPreferencesFormatException(e);
2.342 + }
2.343 + */
2.344 + }
2.345 +/*
2.346 + private static class Resolver implements EntityResolver {
2.347 + public InputSource resolveEntity(String pid, String sid)
2.348 + throws SAXException
2.349 + {
2.350 + if (sid.equals(PREFS_DTD_URI)) {
2.351 + InputSource is;
2.352 + is = new InputSource(new StringReader(PREFS_DTD));
2.353 + is.setSystemId(PREFS_DTD_URI);
2.354 + return is;
2.355 + }
2.356 + throw new SAXException("Invalid system identifier: " + sid);
2.357 + }
2.358 + }
2.359 +
2.360 + private static class EH implements ErrorHandler {
2.361 + public void error(SAXParseException x) throws SAXException {
2.362 + throw x;
2.363 + }
2.364 + public void fatalError(SAXParseException x) throws SAXException {
2.365 + throw x;
2.366 + }
2.367 + public void warning(SAXParseException x) throws SAXException {
2.368 + throw x;
2.369 + }
2.370 + }
2.371 +*/
2.372 + private static boolean isRemoved(Preferences p) {
2.373 + try {
2.374 + p.parent(); // throws IllegalStateException if removed;
2.375 + return false;
2.376 + } catch (IllegalStateException ex) {
2.377 + return true;
2.378 + }
2.379 + }
2.380 +
2.381 + private static Object lock(Preferences p) {
2.382 + /** JST-XXX: Needs reflection or accessor:
2.383 + * http://wiki.apidesign.org/wiki/FriendPackages
2.384 + return ((AbstractPreferences)prefs).lock;
2.385 + */
2.386 + return p;
2.387 + }
2.388 +}
3.1 --- a/src/share/classes/sun/util/xml/PrefsXmlSupport.java Wed Jun 24 16:38:24 2009 +0200
3.2 +++ b/src/share/classes/sun/util/xml/PrefsXmlSupport.java Wed Jun 24 17:29:29 2009 +0200
3.3 @@ -40,7 +40,7 @@
3.4 public abstract class PrefsXmlSupport {
3.5 public static PrefsXmlSupport getDefault() {
3.6 Iterator<PrefsXmlSupport> it = ServiceLoader.load(PrefsXmlSupport.class).iterator();
3.7 - return it.hasNext() ? it.next() : null /* JST-XXX: some default */;
3.8 + return it.hasNext() ? it.next() : new DefaultPrefsXmlSupport();
3.9 }
3.10
3.11 public abstract void export(OutputStream os, final Preferences p, boolean subTree)
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/test/java/util/prefs/Preferences/XMLPreferencesTest.java Wed Jun 24 17:29:29 2009 +0200
4.3 @@ -0,0 +1,97 @@
4.4 +/*
4.5 + * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4.7 + *
4.8 + * This code is free software; you can redistribute it and/or modify it
4.9 + * under the terms of the GNU General Public License version 2 only, as
4.10 + * published by the Free Software Foundation. Sun designates this
4.11 + * particular file as subject to the "Classpath" exception as provided
4.12 + * by Sun in the LICENSE file that accompanied this code.
4.13 + *
4.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
4.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4.17 + * version 2 for more details (a copy is included in the LICENSE file that
4.18 + * accompanied this code).
4.19 + *
4.20 + * You should have received a copy of the GNU General Public License version
4.21 + * 2 along with this work; if not, write to the Free Software Foundation,
4.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4.23 + *
4.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
4.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
4.26 + * have any questions.
4.27 + */
4.28 +
4.29 +
4.30 +import java.io.ByteArrayInputStream;
4.31 +import sun.util.xml.PrefsXmlSupport;
4.32 +
4.33 +import java.io.ByteArrayOutputStream;
4.34 +import java.util.prefs.Preferences;
4.35 +
4.36 +/** Checks whether reading and writing via standard DOM and simplified API
4.37 + * results in same results.
4.38 + *
4.39 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.40 + */
4.41 +public class XMLPreferencesTest {
4.42 + private static PrefsXmlSupport FULL = new com.sun.xml.internal.PrefsXmlSupportImpl();
4.43 + private static PrefsXmlSupport SIMPLE = new sun.util.xml.DefaultPrefsXmlSupport();
4.44 +
4.45 +
4.46 + public static void main(String[] args) throws Exception {
4.47 + XMLPreferencesTest test = new XMLPreferencesTest();
4.48 + test.testCompareOutput();
4.49 + test.testCompareInput();
4.50 + }
4.51 +
4.52 +
4.53 + public XMLPreferencesTest() {
4.54 + }
4.55 +
4.56 + public void testCompareOutput() throws Exception {
4.57 + Preferences p = Preferences.userRoot().node("a/b/c");
4.58 + p.put("ahoj", "simple");
4.59 + p.putInt("kuk", 1);
4.60 + p.putBoolean("multi", true);
4.61 + p.node("child").putBoolean("data", false);
4.62 + p.node("empty");
4.63 + p.parent().putDouble("visible", 1.0);
4.64 +
4.65 + ByteArrayOutputStream full = new ByteArrayOutputStream();
4.66 + FULL.export(full, p, true);
4.67 +
4.68 + ByteArrayOutputStream simple = new ByteArrayOutputStream();
4.69 + SIMPLE.export(simple, p, true);
4.70 + if (full.toString().equals(simple.toString())) {
4.71 + // OK
4.72 + System.err.println("OK: testCompareOutput");
4.73 + } else {
4.74 + assert false :
4.75 + "Full version differs from simplified. Full:\n" + full + "\nSimple:\n" + simple;
4.76 + }
4.77 + }
4.78 +
4.79 + public void testCompareInput() throws Exception {
4.80 + /*
4.81 + String text = "";
4.82 +
4.83 + Properties p1 = new Properties();
4.84 + {
4.85 + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8"));
4.86 + FULL.load(p1, is);
4.87 + }
4.88 +
4.89 + Properties p2 = new Properties();
4.90 + {
4.91 + ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes("UTF-8"));
4.92 + SIMPLE.load(p2, is);
4.93 + }
4.94 +
4.95 + assert p1.equals(p2) : "P1: " + p1 + "\nP2: " + p2;
4.96 + System.err.println("OK: testCompareInput");
4.97 + */
4.98 + }
4.99 +
4.100 +}
4.101 \ No newline at end of file