KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > settings > DOMConvertor


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 2002-2003 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.spi.settings;
21
22 import java.io.IOException JavaDoc;
23 import java.util.Map JavaDoc;
24 import org.openide.filesystems.FileObject;
25 import org.openide.util.Lookup;
26 import org.openide.xml.XMLUtil;
27 import org.w3c.dom.Document JavaDoc;
28 import org.xml.sax.InputSource JavaDoc;
29 import org.xml.sax.SAXException JavaDoc;
30
31 /** DOMConvertor extends the Convertor to allow to compose output of several
32  * convertors into one xml document. For more info see
33  * <a HREF='./doc-files/api.html#use-composed'>Composed Content</a>.
34  *
35  * @author Jan Pokorsky
36  * @since 1.1
37  */

38 public abstract class DOMConvertor extends Convertor {
39     /** an attribute containing public ID of DTD describing nested elements */
40     private final static String JavaDoc ATTR_PUBLIC_ID = "dtd_public_id"; // NOI18N
41

42     private final static String JavaDoc ATTR_ID = "id"; // NOI18N
43
private final static String JavaDoc ATTR_IDREF = "idref"; // NOI18N
44
/** element used to wrap output from non-DOMConvertor or to reference
45      * already written object.
46      * @see #ATTR_ID
47      * @see #ATTR_IDREF
48      */

49     // Usage:
50
// <domconvertor dtd_public_id='...'><[!CDATA[...]]></domconvertor>
51
// or
52
// ...
53
// <foo id='1' dtd_public_id='...'>...</foo>
54
// ...
55
// <domconvertor idref='1'/>
56

57     private final static String JavaDoc ELM_DELEGATE = "domconvertor"; // NOI18N
58

59     private final static java.util.Map JavaDoc<Document JavaDoc, Map JavaDoc<Object JavaDoc,CacheRec>> refsCache =
60             new java.util.HashMap JavaDoc<Document JavaDoc, Map JavaDoc<Object JavaDoc,CacheRec>>();
61     /** cache of contexts <Document, Lookup>*/
62     private final static java.util.Map JavaDoc<Document JavaDoc, Lookup> ctxCache = new java.util.HashMap JavaDoc<Document JavaDoc, Lookup>();
63     
64     private String JavaDoc publicID;
65     private String JavaDoc systemID;
66     private String JavaDoc rootElement;
67     
68     /** Creat a DOMConvertor
69      * @param publicID public ID of DOCTYPE
70      * @param systemID system ID of DOCTYPE
71      * @param rootElement qualified name of root element
72      */

73     protected DOMConvertor(String JavaDoc publicID, String JavaDoc systemID, String JavaDoc rootElement) {
74         this.publicID = publicID;
75         this.systemID = systemID;
76         this.rootElement = rootElement;
77         if (publicID == null) throw new NullPointerException JavaDoc("publicID"); // NOI18N
78
if (systemID == null) throw new NullPointerException JavaDoc("systemID"); // NOI18N
79
if (rootElement == null) throw new NullPointerException JavaDoc("rootElement"); // NOI18N
80
}
81     
82     /** Read content from <code>r</code> and delegate to {@link #readElement}
83      * passing parsed content as a root element of DOM document
84      * @param r stream containing stored object
85      * @return the read setting object
86      * @exception IOException if the object cannot be read
87      * @exception ClassNotFoundException if the object class cannot be resolved
88      * @since 1.1
89      */

90     public final Object JavaDoc read(java.io.Reader JavaDoc r) throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
91         Document JavaDoc doc = null;
92         try {
93             InputSource JavaDoc is = new InputSource JavaDoc(r);
94             doc = XMLUtil.parse(is, false, false, null, org.openide.xml.EntityCatalog.getDefault());
95             setDocumentContext(doc, findContext(r));
96             return readElement(doc.getDocumentElement());
97         } catch (SAXException JavaDoc ex) {
98             IOException JavaDoc ioe = new IOException JavaDoc(ex.getLocalizedMessage());
99             ioe.initCause(ex);
100             throw ioe;
101         } finally {
102             if (doc != null) {
103                 clearCashesForDocument(doc);
104             }
105         }
106     }
107     
108     /** Write object described by DOM document filled by {@link #writeElement}
109      * @param w stream into which inst is written
110      * @param inst the setting object to be written
111      * @exception IOException if the object cannot be written
112      * @since 1.1
113      */

114     public final void write(java.io.Writer JavaDoc w, Object JavaDoc inst) throws java.io.IOException JavaDoc {
115         Document JavaDoc doc = null;
116         try {
117             doc = XMLUtil.createDocument(rootElement, null, publicID, systemID);
118             setDocumentContext(doc, findContext(w));
119             writeElement(doc, doc.getDocumentElement(), inst);
120             java.io.ByteArrayOutputStream JavaDoc baos = new java.io.ByteArrayOutputStream JavaDoc(1024);
121             XMLUtil.write(doc, baos, "UTF-8"); // NOI18N
122
w.write(baos.toString("UTF-8")); // NOI18N
123
} catch (org.w3c.dom.DOMException JavaDoc ex) {
124             IOException JavaDoc e = new IOException JavaDoc(ex.getLocalizedMessage());
125             e.initCause(ex);
126             throw e;
127         } finally {
128             if (doc != null) {
129                 clearCashesForDocument(doc);
130             }
131         }
132     }
133     
134     /** Provide an object constructed from the element.
135      * @param element represents a read object in a DOM document
136      * @return an setting object
137      * @exception IOException if the object cannot be read
138      * @exception ClassNotFoundException if the object class cannot be resolved
139      * @since 1.1
140      */

141     protected abstract Object JavaDoc readElement(org.w3c.dom.Element JavaDoc element) throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc;
142     
143     /** Fill a DOM element describing <code>obj</code> with attributes or subelements.
144      * @param doc a DOM document allowing to create elements describing passed object
145      * @param element represents a written object in a DOM document
146      * @param obj an object to convert
147      * @exception IOException if the object cannot be written
148      * @exception org.w3c.dom.DOMException if an element construction failed
149      * @since 1.1
150      */

151     protected abstract void writeElement(
152         org.w3c.dom.Document JavaDoc doc,
153         org.w3c.dom.Element JavaDoc element,
154         Object JavaDoc obj) throws java.io.IOException JavaDoc, org.w3c.dom.DOMException JavaDoc;
155
156     /** delegate the read operation to a convertor referenced by <code>dtd_public_id</code>
157      * @param element DOM element that should be read
158      * @return an setting object
159      * @exception IOException if the object cannot be read
160      * @exception ClassNotFoundException if the object class cannot be resolved
161      * @see #readElement
162      * @since 1.1
163      */

164     protected final static Object JavaDoc delegateRead(org.w3c.dom.Element JavaDoc element) throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
165         Object JavaDoc obj;
166         
167         // in case of a reference a cache of already read objects should
168
// be consulted instead of delegating
169
String JavaDoc idref = element.getAttribute(ATTR_IDREF);
170         if (idref.length() != 0) {
171             obj = getCache(element.getOwnerDocument(), idref.intern());
172             if (obj != null) {
173                 return obj;
174             } else {
175                 throw new IOException JavaDoc("broken reference: " + element + ", idref=" + idref); // NOI18N
176
}
177         }
178         
179         // lookup convertor
180
String JavaDoc publicId = element.getAttribute(ATTR_PUBLIC_ID);
181         Convertor c = ConvertorResolver.getDefault().getConvertor(publicId);
182         if (c == null) throw new IOException JavaDoc("Convertor not found. publicId: " + publicId); // NOI18N
183

184         // read
185
if (element.getTagName().equals(ELM_DELEGATE)) {
186             // read CDATA block
187
org.w3c.dom.NodeList JavaDoc children = element.getChildNodes();
188             String JavaDoc content = null;
189             for (int i = 0, size = children.getLength(); i < size; i++) {
190                 org.w3c.dom.Node JavaDoc n = children.item(i);
191                 if (n.getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) {
192                     content = n.getNodeValue();
193                     break;
194                 } else if (n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
195                     // #62018: some serializers may not produce CDATA.
196
String JavaDoc text = n.getNodeValue().trim();
197                     if (text.length() > 0) {
198                         content = text;
199                         break;
200                     }
201                 }
202             }
203             
204             if (content == null) {
205                 throw new IOException JavaDoc("Expected CDATA block under: " + // NOI18N
206
element.getTagName());
207             }
208             obj = readFromString(c, content, findContext(element.getOwnerDocument()));
209         } else if (c instanceof DOMConvertor) {
210             DOMConvertor dc = (DOMConvertor) c;
211             obj = dc.readElement(element);
212         } else {
213             throw new IOException JavaDoc(
214                 "Missing DOMConvertor for publicId: " + publicId); // NOI18N
215
}
216         
217         // cache reference
218
String JavaDoc id = element.getAttribute(ATTR_ID);
219         if (id.length() != 0) {
220             setCache(element.getOwnerDocument(), id, obj);
221         }
222         
223         return obj;
224     }
225     
226     /** delegate the write operation to a convertor able to write <code>obj<code>.
227      * @param doc a DOM document allowing to create elements describing passed object
228      * @param obj an object to convert
229      * @return a DOM element representation
230      * @exception IOException if the object cannot be written
231      * @exception org.w3c.dom.DOMException if an element construction failed
232      * @see #writeElement
233      * @since 1.1
234      */

235     protected final static org.w3c.dom.Element JavaDoc delegateWrite(org.w3c.dom.Document JavaDoc doc, Object JavaDoc obj) throws java.io.IOException JavaDoc, org.w3c.dom.DOMException JavaDoc {
236         // first lookup a cache of already written objects to prevent
237
// storing of the same instance multiple times.
238
CacheRec cache = setCache(doc, obj);
239         if (cache.used) {
240             return writeReference(doc, cache);
241         }
242         
243         ConvertorResolver res = ConvertorResolver.getDefault();
244         Class JavaDoc clazz = obj.getClass();
245         Convertor c = res.getConvertor(clazz);
246         if (c == null) {
247             throw new IOException JavaDoc("Convertor not found for object: " + obj); // NOI18N
248
}
249         
250         org.w3c.dom.Element JavaDoc el;
251         if (c instanceof DOMConvertor) {
252             DOMConvertor dc = (DOMConvertor) c;
253             el = doc.createElement(dc.rootElement);
254             dc.writeElement(doc, el, obj);
255             if (el.getAttribute(ATTR_PUBLIC_ID).length() == 0) {
256                 el.setAttribute(ATTR_PUBLIC_ID, res.getPublicID(clazz));
257             }
258         } else {
259             // plain convertor -> wrap content to CDATA block
260
el = doc.createElement(ELM_DELEGATE);
261             el.setAttribute(ATTR_PUBLIC_ID, res.getPublicID(clazz));
262             el.appendChild(doc.createCDATASection(writeToString(c, obj, findContext(doc))));
263         }
264         
265         // bind cached object with original element
266
cache.elm = el;
267         return el;
268     }
269     
270     /** get a context associated with the document <code>doc</code>. It can
271      * contain various info like a file location of the read document etc.
272      * @param doc a DOM document containing stored object
273      * @return a context associated with the document
274      * @since 1.2
275      */

276     protected static org.openide.util.Lookup findContext(org.w3c.dom.Document JavaDoc doc) {
277         synchronized (ctxCache) {
278             Lookup ctx = ctxCache.get(doc);
279             return ctx == null? Lookup.EMPTY: ctx;
280         }
281     }
282     
283    
284     // private impl //////////////////////////////////////////////////////////////
285

286     /** remember context for document */
287     private static void setDocumentContext(Document JavaDoc doc, Lookup ctx) {
288         synchronized (ctxCache) {
289             ctxCache.put(doc, ctx);
290         }
291     }
292     
293     /** write an object obj to String using Convertor.write() */
294     private static String JavaDoc writeToString(Convertor c, Object JavaDoc obj, Lookup ctx) throws IOException JavaDoc {
295         java.io.Writer JavaDoc caw = new java.io.CharArrayWriter JavaDoc(1024);
296         java.io.Writer JavaDoc w = caw;
297         
298         FileObject fo = (FileObject) ctx.lookup(FileObject.class);
299         if (fo != null) {
300             w = org.netbeans.modules.settings.ContextProvider.createWriterContextProvider(caw, fo);
301         }
302         
303         c.write(w, obj);
304         w.close();
305         return caw.toString();
306     }
307     
308     /** read an object from String using Convertor.read() */
309     private static Object JavaDoc readFromString(Convertor c, String JavaDoc s, Lookup ctx) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
310         java.io.Reader JavaDoc r = new java.io.StringReader JavaDoc(s);
311         
312         FileObject fo = (FileObject) ctx.lookup(FileObject.class);
313         if (fo != null) {
314             r = org.netbeans.modules.settings.ContextProvider.createReaderContextProvider(r, fo);
315         }
316         
317         return c.read(r);
318     }
319     
320     /** create an element referencing an already stored object */
321     private static org.w3c.dom.Element JavaDoc writeReference(org.w3c.dom.Document JavaDoc doc, CacheRec cache) throws org.w3c.dom.DOMException JavaDoc {
322         org.w3c.dom.Element JavaDoc el = doc.createElement(cache.elm.getTagName());
323         el.setAttribute(ATTR_IDREF, (String JavaDoc) cache.value);
324         cache.elm.setAttribute(ATTR_ID, (String JavaDoc) cache.value);
325         return el;
326     }
327     
328     /** remember an object obj being stored in document key and generate its ID
329      * in scope of the document. Used during write operations.
330      */

331     private static CacheRec setCache(org.w3c.dom.Document JavaDoc key, Object JavaDoc obj) {
332         synchronized (refsCache) {
333             Map JavaDoc<Object JavaDoc, CacheRec> refs = refsCache.get(key);
334             if (refs == null) {
335                 refs = new java.util.HashMap JavaDoc<Object JavaDoc, CacheRec>();
336                 refsCache.put(key, refs);
337             }
338             
339             CacheRec cr = refs.get(obj);
340             if (cr == null) {
341                 cr = new CacheRec();
342                 cr.key = obj;
343                 cr.value = "ID_" + String.valueOf(refs.size()); // NOI18N
344
refs.put(obj, cr);
345             }
346             cr.used = cr.elm != null;
347             return cr;
348         }
349     }
350     
351     /** remember an object obj and its ID being stored in document key to allow
352      * to resolve stored references in scope of the document.
353      * Used during read operations.
354      * @see #getCache
355      */

356     private static CacheRec setCache(org.w3c.dom.Document JavaDoc key, Object JavaDoc id, Object JavaDoc obj) {
357         synchronized (refsCache) {
358             Map JavaDoc<Object JavaDoc, CacheRec> refs = refsCache.get(key);
359             if (refs == null) {
360                 refs = new java.util.HashMap JavaDoc<Object JavaDoc, CacheRec>();
361                 refsCache.put(key, refs);
362             }
363             
364             CacheRec cr = refs.get(id);
365             if (cr == null) {
366                 cr = new CacheRec();
367                 cr.key = id;
368                 cr.value = obj;
369                 refs.put(id, cr);
370             }
371             return cr;
372         }
373     }
374     
375     /** resolve a reference idref in scope of the document key.
376      * Used during read operations.
377      */

378     private static Object JavaDoc getCache(org.w3c.dom.Document JavaDoc key, Object JavaDoc idref) {
379         synchronized (refsCache) {
380             java.util.Map JavaDoc refs = (java.util.Map JavaDoc) refsCache.get(key);
381             if (refs == null) {
382                 return null;
383             }
384             
385             CacheRec cr = (CacheRec) refs.get(idref);
386             return cr.value;
387         }
388     }
389     
390     /** clears cashes per DOM document. Use when an object is converted. */
391     private static void clearCashesForDocument(Document JavaDoc doc) {
392         synchronized(refsCache) {
393             refsCache.remove(doc);
394         }
395         synchronized(ctxCache) {
396             ctxCache.remove(doc);
397         }
398     }
399     
400     private static class CacheRec {
401         CacheRec() {}
402         // key/value are paired as id/settings_object or settings_object/id
403
// depends on performed operation (read/write)
404
Object JavaDoc key;
405         org.w3c.dom.Element JavaDoc elm;
406         Object JavaDoc value;
407         boolean used;
408     }
409 }
410
Popular Tags