1 /* NodeWriter - Writes and exports preferences nodes to files
2    Copyright (C) 2001, 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.java.util.prefs;
39 
40 import gnu.java.lang.CPStringBuilder;
41 
42 import java.io.BufferedWriter;
43 import java.io.IOException;
44 import java.io.OutputStream;
45 import java.io.OutputStreamWriter;
46 import java.io.UnsupportedEncodingException;
47 import java.io.Writer;
48 
49 import java.util.StringTokenizer;
50 
51 import java.util.prefs.*;
52 
53 /**
54  * Writes and exports preferences nodes to files
55  *
56  * @author Mark Wielaard (mark@klomp.org)
57  */
58 public class NodeWriter {
59 
60     /** The Preferences node to write. */
61     private final Preferences prefs;
62 
63     /** The bufferedWriter to write the node to. */
64     private final BufferedWriter bw;
65 
66     /**
67      * True if the complete sub tree should be written,
68      * false if only the node should be written.
69      */
70     private boolean subtree;
71 
72     /**
73      * Creates a new NodeWriter for the given preferences node and
74      * outputstream. Creates a new OutputStreamWriter.
75      */
NodeWriter(Preferences prefs, OutputStream os)76     public NodeWriter(Preferences prefs, OutputStream os) {
77         this.prefs = prefs;
78         Writer w;
79         try
80           {
81             w = new OutputStreamWriter(os, "UTF-8");
82           }
83         catch (UnsupportedEncodingException uee)
84           {
85             // Shouldn't happen, since we always have UTF-8 available.
86             InternalError ie = new InternalError("UTF-8 encoding missing");
87             ie.initCause(uee);
88             throw ie;
89           }
90         this.bw = new BufferedWriter(w);
91     }
92 
93     /**
94      * Writes the preference node plus the complete subtree.
95      */
writePrefsTree()96     public void writePrefsTree() throws BackingStoreException, IOException {
97         subtree = true;
98         writeHeader();
99         writePreferences();
100         bw.flush();
101     }
102 
103     /**
104      * Writes only the preference node.
105      */
writePrefs()106     public void writePrefs() throws BackingStoreException, IOException {
107         subtree = false;
108         writeHeader();
109         writePreferences();
110         bw.flush();
111     }
112 
113     /**
114      * Writes the standard header.
115      */
writeHeader()116     private void writeHeader() throws BackingStoreException, IOException {
117         bw.write("<?xml version=\"1.0\"?>");
118         bw.newLine();
119         bw.write("<!DOCTYPE preferences SYSTEM "
120                  + "\"http://java.sun.com/dtd/preferences.dtd\">");
121         bw.newLine();
122         bw.newLine();
123         bw.write("<!-- GNU Classpath java.util.prefs Preferences ");
124 
125         if (prefs.isUserNode()) {
126             bw.write("user");
127         } else {
128             bw.write("system");
129         }
130 
131         // root node?
132         if (prefs.parent() == null) {
133             bw.write(" root");
134         }
135 
136         if (subtree) {
137             bw.write(" tree");
138         } else {
139             bw.write(" node");
140         }
141 
142         // no root?
143         if (prefs.parent() != null) {
144             bw.newLine();
145             bw.write("     '");
146             bw.write(prefs.absolutePath());
147             bw.write('\'');
148             bw.newLine();
149         }
150         bw.write(" -->");
151         bw.newLine();
152         bw.newLine();
153     }
154 
155     /**
156      * Write the preferences tag and the root.
157      */
writePreferences()158     private void writePreferences() throws BackingStoreException, IOException {
159         bw.write("<preferences>");
160         bw.newLine();
161         writeRoot();
162         bw.write("</preferences>");
163         bw.newLine();
164     }
165 
writeRoot()166     private void writeRoot() throws BackingStoreException, IOException {
167         bw.write("  <root type=\"");
168         if (prefs.isUserNode()) {
169             bw.write("user");
170         } else {
171             bw.write("system");
172         }
173         bw.write("\">");
174 
175         writeRootMap();
176         writeNode();
177 
178         bw.write("  </root>");
179         bw.newLine();
180     }
181 
writeRootMap()182     private void writeRootMap() throws BackingStoreException, IOException {
183         // Is it a root node?
184         if(prefs.parent() == null && prefs.keys().length > 0) {
185             bw.newLine();
186             writeMap(prefs, 2);
187         } else {
188             bw.write("<map/>");
189             bw.newLine();
190         }
191     }
192 
193     /**
194      * Writes all the parents of the preferences node without any entries.
195      * Returns the number of parents written, which has to be used as
196      * argument to <code>writeCloseParents()</code> after writing the node
197      * itself.
198      */
writeParents()199     private int writeParents() throws IOException {
200         int parents;
201         String path = prefs.absolutePath();
202         int lastslash = path.lastIndexOf("/");
203         if (lastslash > 0) {
204             path = path.substring(1, lastslash);
205             StringTokenizer st = new StringTokenizer(path);
206             parents = st.countTokens();
207 
208             for (int i=0; i<parents; i++) {
209                 String name = st.nextToken();
210                 indent(i+2);
211                 bw.write("<node name=\"" + name + "\">");
212                 bw.write("<map/>");
213                 bw.write("</node>");
214                 bw.newLine();
215             }
216         } else {
217             parents = 0;
218         }
219 
220         return parents;
221     }
222 
writeCloseParents(int parents)223     private void writeCloseParents(int parents) throws IOException {
224         while(parents > 0) {
225             indent(parents+1);
226             bw.write("</node>");
227             bw.newLine();
228             parents--;
229         }
230     }
231 
writeNode()232     private void writeNode() throws BackingStoreException, IOException {
233         int parents = writeParents();
234         // root?
235         int indent;
236         if (prefs.parent() == null) {
237             indent = parents+1;
238         } else {
239             indent = parents+2;
240         }
241         writeNode(prefs, indent);
242         writeCloseParents(parents);
243     }
244 
writeNode(Preferences node, int indent)245     private void writeNode(Preferences node, int indent)
246                                     throws BackingStoreException, IOException
247     {
248         // not root?
249         if (node.parent() != null) {
250             indent(indent);
251             bw.write("<node name=\"" + node.name() + "\">");
252             if (node.keys().length > 0) {
253                 bw.newLine();
254             }
255             writeMap(node, indent+1);
256         }
257 
258         if (subtree) {
259             String[] children = node.childrenNames();
260             for (int i=0; i<children.length; i++) {
261                 Preferences child = node.node(children[i]);
262                 writeNode(child, indent+1);
263             }
264         }
265 
266         // not root?
267         if (node.parent() != null) {
268             indent(indent);
269             bw.write("</node>");
270             bw.newLine();
271         }
272     }
273 
writeMap(Preferences node, int indent)274     private void writeMap(Preferences node, int indent)
275                                     throws BackingStoreException, IOException
276     {
277         // construct String used for indentation
278         CPStringBuilder indentBuffer = new CPStringBuilder(2*indent);
279         for (int i=0; i < indent; i++)
280             indentBuffer.append("  ");
281         String indentString = indentBuffer.toString();
282 
283         if (node.keys().length > 0) {
284             bw.write(indentString);
285             bw.write("<map>");
286             bw.newLine();
287             writeEntries(node, indentString + "  ");
288             bw.write(indentString);
289             bw.write("</map>");
290         } else {
291             bw.write("<map/>");
292         }
293         bw.newLine();
294     }
295 
writeEntries(Preferences node, String indent)296     private void writeEntries(Preferences node, String indent)
297                                     throws BackingStoreException, IOException
298     {
299         String[] keys = node.keys();
300         for(int i = 0; i < keys.length; i++) {
301             String value = node.get(keys[i], null);
302             if (value == null) {
303                 throw new BackingStoreException("null value for key '"
304                                                 + keys[i] + "'");
305             }
306 
307             bw.write(indent);
308             bw.write("<entry key=\"" + keys[i] + "\""
309                     + " value=\"" + value + "\"/>");
310             bw.newLine();
311         }
312     }
313 
indent(int x)314     private void indent(int x) throws IOException {
315         for (int i=0; i<x; i++) {
316             bw.write("  ");
317         }
318     }
319 }
320