KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > loaders > XMLDataObject


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 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.loaders;
21
22
23 import java.io.*;
24 import java.lang.ref.*;
25 import java.lang.reflect.*;
26 import java.net.URL JavaDoc;
27 import java.util.*;
28 import java.util.logging.*;
29 import javax.swing.Action JavaDoc;
30 import javax.xml.parsers.DocumentBuilder JavaDoc;
31 import org.openide.actions.OpenAction;
32 import org.openide.cookies.*;
33 import org.openide.filesystems.*;
34 import org.openide.nodes.*;
35 import org.openide.text.DataEditorSupport;
36 import org.openide.util.*;
37 import org.openide.util.actions.SystemAction;
38 import org.openide.util.lookup.AbstractLookup;
39 import org.openide.windows.CloneableOpenSupport;
40 import org.openide.xml.*;
41 import org.w3c.dom.Document JavaDoc;
42 import org.w3c.dom.DocumentType JavaDoc;
43 import org.xml.sax.*;
44 import org.xml.sax.ext.LexicalHandler JavaDoc;
45 import org.xml.sax.helpers.DefaultHandler JavaDoc;
46
47 /**
48  * Object that provides main functionality for xml documents.
49  * These objects are recognized by the <code>xml</code> extension and
50  * <code>text/xml</code> MIME type.
51  * <p>
52  * It is declaratively extensible by an {@link Environment}.
53  * The <code>Environment</code> is assigned to document instances using a provider
54  * registered by DOCTYPE's public ID in the system filesystem under
55  * <code>xml/lookups/{Transformed-DOCTYPE}</code> where the DOCTYPE transformation
56  * is the same as that defined for {@link EntityCatalog} registrations.
57  *
58  * @see XMLUtil
59  * @see EntityCatalog
60  *
61  * @author Libor Kramolis, Jaroslav Tulach, Petr Kuzel
62  */

63 public class XMLDataObject extends MultiDataObject {
64      /** generated Serialized Version UID */
65     static final long serialVersionUID = 8757854986453256578L;
66     
67    /** Public ID of xmlinfo dtd.
68     * @deprecated replaced with Lookup
69     */

70     @Deprecated JavaDoc
71     public static final String JavaDoc XMLINFO_DTD_PUBLIC_ID_FORTE = "-//Forte for Java//DTD xmlinfo//EN"; // NOI18N
72
/** @deprecated replaced with Lookup
73      */

74     @Deprecated JavaDoc
75     public static final String JavaDoc XMLINFO_DTD_PUBLIC_ID = "-//NetBeans IDE//DTD xmlinfo//EN"; // NOI18N
76

77     /** Mime type of XML documents. */
78     public static final String JavaDoc MIME = "text/xml"; //NOI18N
79
//public static final String MIME2 = "application/xml"; //NOI18N
80

81     /** PROP_DOCUMENT not parsed yet. Constant for getStatus method. */
82     public static final int STATUS_NOT = 0;
83     /** PROP_DOCUMENT parsed ok. Constant for getStatus method. */
84     public static final int STATUS_OK = 1;
85     /** PROP_DOCUMENT parsed with warnings. Constant for getStatus method. */
86     public static final int STATUS_WARNING = 2;
87     /** PROP_DOCUMENT parsed with errors. Constant for getStatus method. */
88     public static final int STATUS_ERROR = 3;
89     
90     /** property name of DOM document property */
91     public static final String JavaDoc PROP_DOCUMENT = "document"; //??? it is not bound well // NOI18N
92

93     /** property name of info property
94      * @deprecated info is not supported anymore. Replaced with lookup.
95      */

96     @Deprecated JavaDoc
97     public static final String JavaDoc PROP_INFO = "info"; // NOI18N
98

99     /** Default XML parser error handler */
100     private static ErrorPrinter errorHandler = new ErrorPrinter();
101         
102     /**
103      * Chain of resolvers contaning all EntityResolvers registred by a user.
104      */

105     private static XMLEntityResolverChain chainingEntityResolver;
106     
107     /** map of DTD publicID => Info. */
108     private static HashMap<String JavaDoc, Info> infos = new HashMap<String JavaDoc, Info>();
109     // the lock can be seamlesly shared by all instances
110
private static Object JavaDoc emgrLock = new Object JavaDoc ();
111     
112     
113     
114     
115     
116     //
117
// Instance variables
118
//
119

120     
121     /** the XML document we delegate to */
122     private DelDoc doc;
123     
124
125     /** the result of parsing */
126     private int status; //??? why it is not a bound property?
127
// it if often out-of date (e.g. garbage collection)
128

129     /** @deprecated EditorCookie provided by subclass support
130      * need to prevail build in cookies.
131      */

132     @Deprecated JavaDoc
133     private EditorCookie editor = null;
134
135     /**
136      * Task body triggered by file change (primaryFile() or xmlinfo) parsing document
137      * for extension (info) assigment information (xmlinfo or public id)
138      */

139     private InfoParser infoParser;
140
141     /* For logging and debugging. */
142     private static final Logger ERR = Logger.getLogger(XMLDataObject.class.getName());
143
144                
145     /**
146      * Create new XMLDataObject. It is usually called by a loader.
147      * A user can get existing XMLDataObject by calling {@link DataObject#find(FileObject)
148      * <code>DataObject.find(FileObject f)</code>} instead.
149      *
150      * @param fo the primary file object, never <code>null</code>
151      * @param loader loader of this data object, never <code>null</code>
152      *
153      */

154     public XMLDataObject (FileObject fo, MultiFileLoader loader)
155     throws DataObjectExistsException {
156         super (fo, loader);
157         
158         fo.addFileChangeListener (FileUtil.weakFileChangeListener (getIP (), fo));
159
160         status = STATUS_NOT;
161
162         // register provided cookies
163
// EditorCookie must be for back compatability consulted with subclasses
164
//
165
// In new model subclasses should directly provide its CookieSet.Factory that
166
// uses last prevails order instead of old CookieSet first prevails order.
167
// It completely prevails over this factory :-)
168

169         CookieSet.Factory factory = new CookieSet.Factory() {
170             public <T extends Node.Cookie> T createCookie(Class JavaDoc<T> klass) {
171                 if (klass.isAssignableFrom(EditorCookie.class)
172                    || klass.isAssignableFrom(OpenCookie.class)
173                    || klass.isAssignableFrom(CloseCookie.class)
174                    || klass.isAssignableFrom(PrintCookie.class) ) {
175                 
176                     if (editor == null) editor = createEditorCookie(); // the first pass
177
if (editor == null) return null; //??? gc unfriendly
178

179                     return klass.isAssignableFrom(editor.getClass()) ? (T)editor : null;
180                 } else {
181                     return null;
182                 }
183             }
184         };
185                 
186         CookieSet cookies = getCookieSet();
187         // EditorCookie.class must be synchronized with
188
// XMLEditor.Env->findCloneableOpenSupport
189
cookies.add(EditorCookie.class, factory);
190         cookies.add(OpenCookie.class, factory);
191         cookies.add(CloseCookie.class, factory);
192         cookies.add(PrintCookie.class, factory);
193         
194         // set info for this file
195
//getIP ().resolveInfo (); #16045
196
}
197
198     /** Getter for info parser. Initializes the infoparser in "lazy" way so it is accessble even before
199         * the constructor finishes.
200         */

201     private final InfoParser getIP () {
202         synchronized (emgrLock) {
203             if (infoParser == null) {
204                 infoParser = new InfoParser ();
205             }
206         }
207         return infoParser;
208     }
209
210
211
212      /** If the Info associated with this data object (if any) provides
213     * a subclass of Node, then this object is created to represent the
214     * XML data object, otherwise DataNode is created.
215     *
216     * @return the node representation for this data object
217     * @see DataNode
218     */

219     protected Node createNodeDelegate () { //??? what about interaction with Looks
220
XMLNode xn = new XMLNode (this);
221         // netbeans.core.nodes.description
222
xn.setShortDescription (NbBundle.getMessage (
223                                         XMLDataObject.class, "HINT_XMLDataObject")); // NOI18N
224
return xn;
225     }
226
227     /** Called when the info file is parsed and the icon should change.
228     * @param res resource for the icon
229     * @deprecated it is better to listen on properties
230     */

231     @Deprecated JavaDoc
232     protected void updateIconBase (String JavaDoc res) {
233         //??? we could add default behaviour, taking status into account
234
}
235
236     /*
237      * Wait until background parsing terminates to avoid concurent file access.
238      * It should terminate very early if just running, we can wait for it.
239      */

240     protected void handleDelete() throws IOException {
241
242         getIP ().waitFinished(); // too late wait for finnish
243
super.handleDelete();
244     }
245
246     public HelpCtx getHelpCtx () {
247         // help for fix #23528, objects represents 'settings' nodes in Options dialog
248
// returns DEFAULT_HELP for next processing
249
try {
250             if (getPrimaryFile ().getFileSystem ().isDefault ()) {
251                 if (getCookie (InstanceCookie.class)!=null) {
252                     return HelpCtx.DEFAULT_HELP;
253                 }
254             }
255         } catch (FileStateInvalidException fsie) {
256             // cannot determine type of this file object ==> return help id as normal
257
}
258         return new HelpCtx (XMLDataObject.class);
259     }
260
261     /**
262      * Cookies from assigned Environment are not placed into
263      * protected CookieSet and can be obtained only by invoking this method.
264      * <p>
265      * Cookie order for Info environments are handled consistently with
266      * CookieSet i.e. FIFO.
267      * @return a cookie (instanceof cls) that has been found in info or
268      * super.getCookie(cls).
269      */

270     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> cls) {
271         getIP ().waitFinished();
272
273         Object JavaDoc cake = getIP().lookupCookie(cls);
274        
275         if (ERR.isLoggable(Level.FINE)) {
276             ERR.fine("Query for " + cls + " for " + this); // NOI18N
277
ERR.fine("Gives a cake " + cake + " for " + this); // NOI18N
278
}
279         
280         if (cake instanceof InstanceCookie) {
281             cake = ofCookie ((InstanceCookie)cake, cls);
282         }
283
284         if (ERR.isLoggable(Level.FINE)) {
285             ERR.fine("After ofCookie: " + cake + " for " + this); // NOI18N
286
}
287         
288         if (cake == null) {
289             cake = super.getCookie (cls);
290         }
291         
292         if (ERR.isLoggable(Level.FINE)) {
293             ERR.fine("getCookie returns " + cake + " for " + this); // NOI18N
294
}
295         
296         return cls.cast(cake);
297     }
298
299     /** Special support of InstanceCookie.Of. If the Info class
300      * provides InstanceCookie but not IC.Of, we add the extra interface to
301      * this data object.
302      *
303      * @param ic instance cookie
304      * @param cls constraining class
305      * @return instance of InstanceCookie.Of
306      */

307     private InstanceCookie ofCookie (InstanceCookie ic, Class JavaDoc cls) {
308         if (ic instanceof InstanceCookie.Of) {
309             return ic;
310         } else if (! cls.isAssignableFrom (ICDel.class)) {
311             // Someone was looking for, and a processor etc. was
312
// providing, some specialization which ICDel cannot
313
// provide. Return the real implementation and forget
314
// about making this a IC.Of.
315
return ic;
316         } else {
317             ICDel d = new ICDel (this, ic);
318             return d;
319         }
320     }
321
322     private void notifyEx(Exception JavaDoc e) {
323         Exceptions.attachLocalizedMessage(e,
324                                           "Cannot resolve following class in xmlinfo."); // NOI18N
325
Exceptions.printStackTrace(e);
326     }
327     
328     /** Allows subclasses to provide their own editor cookie.
329      * @return an editor cookie to be used as a result of <code>getCookie(EditorCookie.class)</code>
330      *
331      * @deprecated CookieSet factory should be used by subclasses instead.
332      */

333     @Deprecated JavaDoc
334     protected EditorCookie createEditorCookie () {
335         return new XMLEditorSupport (this);
336     }
337     
338     // Vertical CookieManager
339
private final void addSaveCookie (SaveCookie save) {
340         getCookieSet ().add (save);
341     }
342     private final void removeSaveCookie (SaveCookie save) {
343         getCookieSet ().remove (save);
344     }
345
346     //??? we ahould add it into class comment to make it public
347
// or should we introduce second layer XMLDataObject extending this one
348
// and having documented this functionality (we cannot because of
349
// so this huge DataObject will survive createEditorCookie())
350
/*
351      * Really simple implementation of OpenCookie, EditorCookie, PrintCookie,
352      * CloseCookie and managing SaveCookie.
353      */

354     private static class XMLEditorSupport extends DataEditorSupport implements OpenCookie, EditorCookie.Observable, PrintCookie, CloseCookie {
355         public XMLEditorSupport (XMLDataObject obj) {
356             super (obj, new XMLEditorEnv (obj));
357             setMIMEType ("text/xml"); // NOI18N
358
}
359         class Save implements SaveCookie {
360             public void save () throws IOException {
361                 saveDocument ();
362                 getDataObject ().setModified (false);
363             }
364         }
365         protected boolean notifyModified () {
366             if (! super.notifyModified ()) {
367                 return false;
368             }
369             if (getDataObject ().getCookie (SaveCookie.class) == null) {
370                 ((XMLDataObject) getDataObject ()).addSaveCookie (new Save ());
371                 getDataObject ().setModified (true);
372             }
373             return true;
374         }
375         protected void notifyUnmodified () {
376             super.notifyUnmodified ();
377             SaveCookie save = (SaveCookie) getDataObject ().getCookie (SaveCookie.class);
378             if (save != null) {
379                 ((XMLDataObject) getDataObject ()).removeSaveCookie (save);
380                 getDataObject ().setModified (false);
381             }
382         }
383         
384         //!!! it also stays for SaveCookie however does not understand
385
// encoding declared in XML header => need to be rewritten.
386
private static class XMLEditorEnv extends DataEditorSupport.Env {
387             private static final long serialVersionUID = 6593415381104273008L;
388             
389             public XMLEditorEnv (DataObject obj) {
390                 super (obj);
391             }
392             protected FileObject getFile () {
393                 return getDataObject ().getPrimaryFile ();
394             }
395             protected FileLock takeLock () throws IOException {
396                 return ((XMLDataObject) getDataObject ()).getPrimaryEntry ().takeLock ();
397             }
398             public CloneableOpenSupport findCloneableOpenSupport () {
399                 // must be sync with cookies.add(EditorCookie.class, factory);
400
// #12938 XML files do not persist in Source editor
401
return (CloneableOpenSupport) getDataObject ().getCookie (EditorCookie.class);
402             }
403         }
404     }
405
406     /** Creates w3c's document for the xml file. Either returns cached reference
407     * or parses the file and creates new document.
408     *
409     * @return the parsed document
410     * @exception SAXException if there is a parsing error
411     * @exception IOException if there is an I/O error
412     */

413     public final Document JavaDoc getDocument () throws IOException, SAXException {
414         if (ERR.isLoggable(Level.FINE)) ERR.fine("getDocument" + " for " + this);
415         synchronized (this) {
416             DelDoc d = doc;
417             if (d == null) {
418                 d = new DelDoc ();
419                 doc = d;
420             }
421             return d.getProxyDocument();
422         }
423     }
424     
425     /** Clears the document. Called when the document file is changed.
426      */

427     final void clearDocument () {
428         if (ERR.isLoggable(Level.FINE)) ERR.fine("clearDocument" + " for " + this);
429         //err.notify (ErrorManager.INFORMATIONAL, new Throwable ("stack dump"));
430
doc = null;
431         firePropertyChange (PROP_DOCUMENT, null, null);
432     }
433
434     /**
435      * @return one of STATUS_XXX constants representing PROP_DOCUMENT state.
436      */

437     public final int getStatus () {
438         return status;
439     }
440
441     /** @deprecated not used anymore
442      * @return null
443      */

444     @Deprecated JavaDoc
445     public final Info getInfo () {
446         return null;
447     }
448
449     /** @deprecated does not do anything useful
450      */

451     @Deprecated JavaDoc
452     public final synchronized void setInfo (Info ii) throws IOException {
453     }
454
455     /** Parses the primary file of this data object.
456     * and provide different implementation.
457     *
458     * @return the document in the primary file
459     * @exception IOException if error during parsing occures
460     */

461     final Document JavaDoc parsePrimaryFile () throws IOException, SAXException {
462         if (ERR.isLoggable(Level.FINE)) ERR.fine("parsePrimaryFile" + " for " + this);
463         String JavaDoc loc = getPrimaryFile().getURL().toExternalForm();
464         try {
465             return XMLUtil.parse(new InputSource(loc), false, /* #36295 */true, errorHandler, getSystemResolver());
466         } catch (IOException e) {
467             // Perhaps this document was not on a mounted filesystem.
468
// Try again with an input stream - no relative URLs will work, but this
469
// is extremely unlikely to matter. Cf. #36340.
470
InputStream is = getPrimaryFile().getInputStream();
471             try {
472                 return XMLUtil.parse(new InputSource(is), false, true, errorHandler, getSystemResolver());
473             } finally {
474                 is.close();
475             }
476         }
477     }
478
479
480     // ~~~~~~~~~~~~~~~~~~~~ Start of Utilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~
481

482     /** Provides access to internal XML parser.
483     * This method takes URL. After successful finish the
484     * document tree is returned. Used non validating parser.
485     *
486     * @param url the url to read the file from
487     * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead
488     * setting null error handler and validation to false.
489     */

490     @Deprecated JavaDoc
491     public static Document JavaDoc parse (URL JavaDoc url) throws IOException, SAXException {
492         return parse (url, errorHandler, false);
493     }
494
495     /** Provides access to internal XML parser.
496     * This method takes URL. After successful finish the
497     * document tree is returned. Used non validating parser.
498     *
499     * @param url the url to read the file from
500     * @param validate if true validating parser is used
501     * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead
502     * setting null handler.
503     */

504     @Deprecated JavaDoc
505     public static Document JavaDoc parse (URL JavaDoc url, boolean validate) throws IOException, SAXException {
506         return parse (url, errorHandler, validate);
507     }
508
509     /** Provides access to internal XML parser.
510     * This method takes URL. After successful finish the
511     * document tree is returned.
512     *
513     * @param url the url to read the file from
514     * @param eh error handler to notify about exception
515     * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead
516     * setting validation to false.
517     */

518     @Deprecated JavaDoc
519     public static Document JavaDoc parse (URL JavaDoc url, ErrorHandler eh) throws IOException, SAXException {
520         return parse (url, eh, false);
521     }
522
523     /** Factory a DocumentBuilder and let it create a org.w3c.dom.Document
524     * This method takes URL. After successful finish the
525     * document tree is returned.
526     * A parser producing the Document has
527     * set entity resolver to system entity resolver chain.
528     *
529     * @param url the url to read the file from
530     * @param eh error handler to notify about exception
531     * @param validate if true validating parser is used
532     * @throws SAXException annotated if thrown due to configuration problem
533     * @throws FactoryConfigurationError
534     * @return org.w3c.dom.Document
535     * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead.
536     */

537     @Deprecated JavaDoc
538     public static Document JavaDoc parse (URL JavaDoc url, ErrorHandler eh, boolean validate) throws IOException, SAXException {
539         
540         DocumentBuilder JavaDoc builder = XMLDataObjectImpl.makeBuilder(validate);
541         builder.setErrorHandler(eh);
542         builder.setEntityResolver(getChainingEntityResolver());
543         
544         return builder.parse (new InputSource(url.toExternalForm()));
545
546     }
547
548     /** Creates SAX parse that can be used to parse XML files.
549      * @return sax parser
550      * @deprecated Use {@link XMLUtil#createXMLReader() XMLUtil} instead.
551      * It will create a SAX XMLReader that is SAX Parser replacement.
552      * You will have to replace DocumentHandler by ContentHandler
553      * besause XMLReader accepts just ContentHandler.
554      * <p>Alternatively if not interested in new callbacks defined by
555      * SAX 2.0 you can wrap returned XMLReader into XMLReaderAdapter
556      * that implements Parser.
557      */

558     @Deprecated JavaDoc
559     public static Parser createParser () {
560         return createParser (false);
561     }
562    
563     
564     /** Factory SAX parser that can be used to parse XML files.
565      * The factory is created according to javax.xml.parsers.SAXParserFactory property.
566      * The parser has set entity resolver to system entity resolver chain.
567      * @param validate if true validating parser is returned
568      * @throws FactoryConfigurationError
569      * @return sax parser or null if no parser can be created
570      * @deprecated Use {@link XMLUtil#createXMLReader(boolean,boolean ) Util} instead
571      * setting ns to false.
572      * For more details see {@link #createParser() createParser}
573      */

574     @Deprecated JavaDoc
575     public static Parser createParser (boolean validate) {
576         
577         Parser parser = XMLDataObjectImpl.makeParser(validate);
578         parser.setEntityResolver(getChainingEntityResolver());
579         return parser;
580         
581     }
582
583
584     /**
585      * Creates empty DOM Document using JAXP factoring.
586      * @return Document or null on problems with JAXP factoring
587      * @deprecated Replaced with {@link XMLUtil#createDocument(String,String,String,String) XMLUtil}
588      * It directly violates DOM's root element reference read-only status.
589      * If you can not move to XMLUtil for compatabilty reasons please
590      * replace with following workaround:
591      * <pre>
592      * String templ = "<myroot/>";
593      * InputSource in = new InputSource(new StringReader(templ));
594      * in.setSystemId("StringReader"); //workaround
595      * DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
596      * Document doc = builder.parse(in);
597      * </pre>
598      */

599     @Deprecated JavaDoc
600     public static Document JavaDoc createDocument() {
601         
602         deprecated();
603         
604         try {
605             return XMLDataObjectImpl.makeBuilder(false).newDocument();
606         } catch (IOException ex) {
607             return null;
608         } catch (SAXException ex) {
609             return null;
610         }
611     }
612
613     /**
614      * Writes DOM Document to writer.
615      *
616      * @param doc DOM Document to be written
617      * @param writer OutoutStreamWriter preffered otherwise
618      * encoding will be left for implementation specific autodection
619      *
620      * @deprecated Encoding used by Writer
621      * may be in direct conflict with encoding
622      * declared in document. Replaced with {@link XMLUtil#write(Document, OutputStream, String) Util}.
623      */

624     @Deprecated JavaDoc
625     public static void write (Document JavaDoc doc, Writer writer) throws IOException {
626
627         deprecated();
628
629         // WARNING: back compatability code
630

631         //use reflection to access "friendly" implementation in other package
632

633         final String JavaDoc FAILURE = "org.openide.xml.XMLUtilImpl.write() invocation failed."; //NOI18N
634

635         try {
636             Class JavaDoc clzz = Class.forName("org.openide.xml.XMLUtilImpl"); //NOI18N
637

638             Method impl = clzz.getDeclaredMethod("write", new Class JavaDoc[] {//NOI18N
639
Document JavaDoc.class, Object JavaDoc.class, String JavaDoc.class
640             });
641             impl.setAccessible(true);
642             impl.invoke(null, new Object JavaDoc[] {doc, writer, null});
643
644         } catch (IllegalAccessException JavaDoc ex) {
645             throw new IOException(FAILURE);
646         } catch (IllegalArgumentException JavaDoc ex) {
647             throw new IOException(FAILURE);
648         } catch (NoSuchMethodException JavaDoc ex) {
649             throw new IOException(FAILURE);
650         } catch (ClassNotFoundException JavaDoc ex) {
651             throw new IOException(FAILURE);
652         } catch (InvocationTargetException ex) {
653             Throwable JavaDoc t = ex.getTargetException();
654             if (t instanceof IOException) {
655                 throw (IOException) t;
656             } else if (t instanceof RuntimeException JavaDoc) {
657                 throw (RuntimeException JavaDoc) t;
658             } else if (t instanceof Error JavaDoc) {
659                 throw (Error JavaDoc) t;
660             }
661             throw new IOException(FAILURE);
662         }
663     }
664
665     /**
666      * Write Document into OutputStream using given encoding.
667      * It is a shortcut for writing configurations etc. It guarantee
668      * just that data will be written. Structure and indentation
669      * may change.
670      *
671      * @param doc DOM Document to be written
672      * @param out data sink
673      * @param enc - XML defined encoding name (i.e. IANA defined, one of UTF-8, UNICODE, ASCII).
674      * @deprecated Moved to {@link XMLUtil#write(Document, OutputStream, String) XMLUtil}.
675      */

676     @Deprecated JavaDoc
677     public static void write(Document JavaDoc doc, OutputStream out, String JavaDoc enc) throws IOException {
678         XMLUtil.write(doc, out, enc);
679     }
680     
681     
682     /**
683      * Creates SAX InputSource for specified URL
684      * @deprecated Deprecated as it was a workaround method. Replace
685      * with <code>new InputSource(url.toExternalForm())</code>.
686      */

687     @Deprecated JavaDoc
688     public static InputSource createInputSource(URL JavaDoc url) throws IOException {
689         return new InputSource(url.toExternalForm());
690     }
691
692     /**
693      * Registers the given public ID as corresponding to a particular
694      * URI, typically a local copy. This URI will be used in preference
695      * to ones provided as system IDs in XML entity declarations. This
696      * mechanism would most typically be used for Document Type Definitions
697      * (DTDs), where the public IDs are formally managed and versioned.
698      *
699      * <P> Any created parser use global entity resolver and you can
700      * register its catalog entry.
701      *
702      * @param publicId The managed public ID being mapped
703      * @param uri The URI of the preferred copy of that entity
704      *
705      * @deprecated Do not rely on global (non-modular) resolvers.
706      * Use {@link EntityCatalog} and {@link XMLUtil}
707      * instead.
708      */

709     @Deprecated JavaDoc
710     public static void registerCatalogEntry (String JavaDoc publicId, String JavaDoc uri) {
711         
712         if (publicId == null)
713             throw new IllegalArgumentException JavaDoc("null public ID is not allowed."); //NOI18N
714

715         XMLDataObjectImpl.registerCatalogEntry(publicId, uri);
716         
717     }
718
719     /**
720      * Registers a given public ID as corresponding to a particular Java
721      * resource in a given class loader, typically distributed with a
722      * software package. This resource will be preferred over system IDs
723      * included in XML documents. This mechanism should most typically be
724      * used for Document Type Definitions (DTDs), where the public IDs are
725      * formally managed and versioned.
726      *
727      * <P> If a mapping to a URI has been provided, that mapping takes
728      * precedence over this one.
729      *
730      * <P> Any created parser use global entity resolver and you can
731      * register its catalog entry.
732      *
733      * @param publicId The managed public ID being mapped
734      * @param resourceName The name of the Java resource
735      * @param loader The class loader holding the resource, or null if
736      * it is a system resource.
737      *
738      * @deprecated Do not rely on global (non-modular) resolvers.
739      * Use {@link EntityCatalog} and {@link XMLUtil}
740      * instead.
741      */

742     @Deprecated JavaDoc
743     public static void registerCatalogEntry (String JavaDoc publicId, String JavaDoc resourceName, ClassLoader JavaDoc loader) {
744         if (publicId == null)
745             throw new IllegalArgumentException JavaDoc("null public ID is not allowed."); //NOI18N
746

747         XMLDataObjectImpl.registerCatalogEntry(publicId, "nbres:/" + resourceName); //NOI18N
748
}
749
750     /**
751      * Add a given entity resolver to the NetBeans resolver chain.
752      * The resolver chain is searched by private chaining resolver
753      * until some registered resolver succed.
754      *
755      * <P>Every created parser use global entity resolver and then chain.
756      *
757      * @deprecated EntityResolver is a parser user responsibility.
758      * Every time set a EntityResolver to an XML parser you use.
759      * The OpenIDE now defines a system {@link EntityCatalog}.
760      *
761      * @param resolver non null resolver to be added
762      *
763      * @return true if successfully added
764      */

765     @Deprecated JavaDoc
766     public static final boolean addEntityResolver(EntityResolver resolver) {
767         // return false; Is is deprecated :-)
768
return getChainingEntityResolver().addEntityResolver(resolver);
769     }
770
771     /**
772      * Remove a given entity resolver from the NetBeans resolver chain.
773      *
774      * <P>Every created parser use global entity resolver and then chain.
775      *
776      * @deprecated EntityResolver is a parser user responsibility.
777      *
778      * @param resolver non null resolver to be removed
779      * @return removed resolver instance or null if not present
780      */

781     @Deprecated JavaDoc
782     public static final EntityResolver removeEntityResolver(EntityResolver resolver) {
783         return getChainingEntityResolver().removeEntityResolver(resolver);
784     }
785
786
787     /** Accessor method for chaining entity resolver implementation. */
788     private static synchronized XMLEntityResolverChain getChainingEntityResolver() {
789
790         if (chainingEntityResolver == null) {
791             chainingEntityResolver = new XMLEntityResolverChain();
792             chainingEntityResolver.addEntityResolver(getSystemResolver());
793         }
794         
795         return chainingEntityResolver;
796         
797     }
798     
799     /** Lazy initialized system resolver. */
800     private static EntityResolver getSystemResolver() {
801         return EntityCatalog.getDefault();
802     }
803     
804     /**
805      * Registers new Info to particular XML document content type as
806      * recognized by DTD public id. The registration is valid until JVM termination.
807      *
808      * @param publicId used as key
809      * @param info associated value or null to unregister
810      *
811      * @deprecated Register an {@link Environment} via lookup, see
812      * {@link XMLDataObject some details}.
813      */

814     @Deprecated JavaDoc
815     public static void registerInfo (String JavaDoc publicId, Info info) { //!!! to be replaced by lookup
816
synchronized (infos) {
817             if (info == null) {
818                 infos.remove(publicId);
819             } else {
820                 infos.put(publicId, info);
821             }
822         }
823     }
824
825     /**
826     * Obtain registered Info for particular DTD public ID.
827     *
828     * @param publicId key which value is required
829     * @return Info clone that is used for given publicId or null
830      *
831      * @deprecated Register via lookup
832     */

833     @Deprecated JavaDoc
834     public static Info getRegisteredInfo(String JavaDoc publicId) { //!!! to be replaced by lookup
835
synchronized (infos) {
836             Info ret = (Info) infos.get(publicId);
837             return ret == null ? null : (Info)ret.clone ();
838         }
839     }
840
841     /*
842      * Guess from stack trace who calls deprecated method and print it.
843      */

844     private static void deprecated() {
845         StringWriter wr = new StringWriter();
846         PrintWriter pr = new PrintWriter(wr);
847         new Exception JavaDoc("").printStackTrace(pr); // NOI18N
848
pr.flush();
849         String JavaDoc stack = wr.toString().trim();
850
851         int start = stack.indexOf("\n"); // NOI18N
852
int end = stack.indexOf("\n", start + 1); // NOI18N
853
while (stack.indexOf("XMLDataObject", start + 1 )>0) { // NOI18N
854
start = end;
855             end = stack.indexOf("\n", start + 1); // NOI18N
856
}
857
858         String JavaDoc line = stack.substring(start + 1, end).trim();
859
860         System.out.println("Warning: deprecated method called " + line); // NOI18N
861
}
862     
863     /**
864      * Default ErrorHandler reporting to log.
865      */

866     static class ErrorPrinter implements ErrorHandler {
867         
868         private void message(final String JavaDoc level, final SAXParseException e) {
869             
870             if (!LOG.isLoggable(Level.FINE)) {
871                 return;
872             }
873             
874             final String JavaDoc msg = NbBundle.getMessage(
875                 XMLDataObject.class,
876                 "PROP_XmlMessage", //NOI18N
877
new Object JavaDoc [] {
878                     level,
879                     e.getMessage(),
880                     e.getSystemId() == null ? "" : e.getSystemId(), // NOI18N
881
"" + e.getLineNumber(), // NOI18N
882
"" + e.getColumnNumber() // NOI18N
883
}
884             );
885                 
886             LOG.fine(msg);
887         }
888
889         public void error(SAXParseException e) {
890             message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlError"), e); //NOI18N
891
}
892
893         public void warning(SAXParseException e) {
894             message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlWarning"), e); //NOI18N
895
}
896
897         public void fatalError(SAXParseException e) {
898             message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlFatalError"), e); //NOI18N
899
}
900     } // end of inner class ErrorPrinter
901

902     
903     //~~~~~~~~~~~~~~~~~~~~~~~~ PARSER ----------------------------------
904

905     // internally stops documet parsing when looking for public id
906
private static class StopSaxException extends SAXException {
907         public StopSaxException() { super("STOP"); } //NOI18N
908
}
909
910     // static fields that that are logically a part of InfoParser
911

912     private static final StopSaxException STOP = new StopSaxException();
913     
914     /** We are guaranteed to be executed in one thread let reuse parser, etc. */
915     private static XMLReader sharedParserImpl = null;
916    
917     static {
918         try {
919             sharedParserImpl = XMLUtil.createXMLReader();
920             sharedParserImpl.setEntityResolver(new EmptyEntityResolver());
921         } catch (SAXException ex) {
922             Exceptions.attachLocalizedMessage(ex,
923                                               "System does not contain JAXP 1.1 compliant parser!"); // NOI18N
924
Logger.getLogger(XMLDataObject.class.getName()).log(Level.WARNING, null, ex);
925         }
926         
927         
928         //initialize stuff possibly needed by libs that do not use
929
//JAXP but SAX 2 directly
930
try {
931             final Properties props = System.getProperties();
932             final String JavaDoc SAX2_KEY = "org.xml.sax.driver"; //NOI18N
933
if (props.getProperty(SAX2_KEY) == null) {
934                 props.put(SAX2_KEY, sharedParserImpl.getClass().getName());
935             }
936         } catch (RuntimeException JavaDoc ex) {
937             //ignore it (we did the best efford)
938
}
939     }
940     
941     /** a string to signal null value for parsedId */
942     private static final String JavaDoc NULL = ""; // NOI18N
943

944     /**
945      * It simulates null that forbiden by SAX specs.
946      */

947     private static class NullHandler extends DefaultHandler JavaDoc implements LexicalHandler JavaDoc {
948
949         static final NullHandler INSTANCE = new NullHandler();
950         
951         NullHandler() {}
952         
953         // LexicalHandler
954

955         public void startDTD(String JavaDoc root, String JavaDoc pID, String JavaDoc sID) throws SAXException {
956         }
957
958         public void endDTD() throws SAXException {
959         }
960
961         public void startEntity(String JavaDoc name) throws SAXException {
962         }
963
964         public void endEntity(String JavaDoc name) throws SAXException {
965         }
966
967         public void startCDATA() throws SAXException {
968         }
969
970         public void endCDATA() throws SAXException {
971         }
972
973         public void comment(char[] ch, int start, int length) throws SAXException {
974         }
975     }
976
977     /**
978      * Parser that parses XML document header to get some hints from it (such as DOCTYPE public ID.
979      * It is named as InfoParser for historical reasons as it originally parsed
980      * a coupled file (with xmlinfo extension) that contained NetBeans-related metadata.
981      * That mechanism was replaced by /xml/lookup/<mangled pid> registrations and
982      * by file attributes.
983      */

984     private final class InfoParser extends DefaultHandler JavaDoc
985     implements FileChangeListener, LexicalHandler JavaDoc, LookupListener {
986             
987         /** the result of parsing the IDE */
988         private String JavaDoc parsedId;
989         
990         /** Lookup associated with this document.
991          */

992         private Lookup lookup;
993         
994         /** result used for this lookup */
995         private Lookup.Result result;
996         
997         private ThreadLocal JavaDoc<Class JavaDoc<?>> QUERY = new ThreadLocal JavaDoc<Class JavaDoc<?>> ();
998         
999         InfoParser() {}
1000        
1001        //~~~~~~~~~~~~~~~~~~~~~ Task body and control of queue ~~~~~~~~~~~~~~~~~~~
1002

1003        /** Getter for public ID of the document.
1004         */

1005        public String JavaDoc getPublicId () {
1006            String JavaDoc id = waitFinished ();
1007            return id == NULL ? null : id;
1008        }
1009        
1010        /** Does lookup for specific cookie. It also
1011         * calls lookup on the result of Node.Cookie.class query and that is why it
1012         * initializes the listeners to notify changes made on the data object.
1013         *
1014         * @param class to look for
1015         */

1016        public Object JavaDoc lookupCookie(final Class JavaDoc<?> clazz) {
1017            if (QUERY.get () == clazz) {
1018                if (ERR.isLoggable(Level.FINE)) ERR.fine("Cyclic deps on queried class: " + clazz + " for " + XMLDataObject.this); // NOI18N
1019
// somebody is querying for the same cookie in the same thread
1020
// probably neverending-loop - ignore
1021
return new InstanceCookie () {
1022                    public Class JavaDoc<?> instanceClass () {
1023                        return clazz;
1024                    }
1025                    
1026                    public Object JavaDoc instanceCreate () throws IOException {
1027                        throw new IOException ("Cyclic reference, sorry: " + clazz);
1028                    }
1029                    
1030                    public String JavaDoc instanceName () {
1031                        return clazz.getName ();
1032                    }
1033                };
1034            }
1035            
1036            Class JavaDoc<?> previous = QUERY.get ();
1037            try {
1038                QUERY.set (clazz);
1039                if (ERR.isLoggable(Level.FINE)) ERR.fine("Will do query for class: " + clazz + " for " + XMLDataObject.this); // NOI18N
1040

1041                Lookup l;
1042                for (;;) {
1043                    String JavaDoc id = waitFinished();
1044                    synchronized (this) {
1045                        if (lookup != null) {
1046                            l = lookup;
1047                        } else {
1048                            l = null;
1049                        }
1050                    }
1051                    if (ERR.isLoggable(Level.FINE)) ERR.fine("Lookup is " + l + " for id: " + id); // NOI18N
1052

1053                    if (l == null) {
1054                        l = updateLookup(null, id);
1055                        if (ERR.isLoggable(Level.FINE)) ERR.fine("Updating lookup: " + l); // NOI18N
1056
}
1057
1058                    if (ERR.isLoggable(Level.FINE)) ERR.fine("Wait lookup is over: " + l + XMLDataObject.this); // NOI18N
1059
if (l != null) {
1060                        break;
1061                    }
1062                    if (parsedId == null) {
1063                        l = Lookup.EMPTY;
1064                        break;
1065                    }
1066                }
1067
1068                Lookup.Result r = result;
1069                if (r != null) {
1070                    if (ERR.isLoggable(Level.FINE)) ERR.fine("Querying the result: " + r); // NOI18N
1071
// just to initialize all listeners
1072
r.allItems ();
1073                } else {
1074                    if (ERR.isLoggable(Level.FINE)) ERR.fine("No result for lookup: " + lookup); // NOI18N
1075
}
1076                Object JavaDoc ret = l.lookup (clazz);
1077                if (ERR.isLoggable(Level.FINE)) ERR.fine("Returning value: " + ret + " for " + XMLDataObject.this); // NOI18N
1078
return ret;
1079            } finally {
1080                QUERY.set (previous);
1081            }
1082        }
1083           
1084        /*
1085         * Find out DTD public ID. Parses the document and updates content of
1086         * parsedId variable.
1087         * @return the parsed ID
1088         */

1089        public String JavaDoc waitFinished () {
1090            return waitFinished (null);
1091        }
1092        
1093        /*
1094         * Find out DTD public ID.
1095         * Info is then assigned according to it from registry.
1096         */

1097        private String JavaDoc waitFinished (String JavaDoc ignorePreviousId) {
1098            if (sharedParserImpl == null) {
1099                ERR.fine("No sharedParserImpl, exiting"); // NOI18N
1100
return NULL;
1101            }
1102            
1103            XMLReader parser = sharedParserImpl;
1104            FileObject myFileObject = getPrimaryFile();
1105            String JavaDoc newID = null;
1106            
1107            if (ERR.isLoggable(Level.FINE)) ERR.fine("Going to read parsedId for " + XMLDataObject.this);
1108            
1109            String JavaDoc previousID;
1110            synchronized (this) {
1111                previousID = parsedId;
1112            }
1113
1114            if (previousID != null) {
1115                if (ERR.isLoggable(Level.FINE)) {
1116                    ERR.fine("Has already been parsed: " + parsedId + " for " + XMLDataObject.this); // NOI18N
1117
}
1118                // ok, has already been parsed
1119
return previousID;
1120            }
1121            
1122            URL JavaDoc url = null;
1123            InputStream in = null;
1124            try {
1125                url = myFileObject.getURL();
1126            } catch (IOException ex) {
1127                warning(ex, "I/O exception while retrieving xml FileObject URL."); //NOI18N
1128
return NULL; // cannot parse
1129
}
1130
1131            synchronized (this) {
1132                try {
1133                    if (!myFileObject.isValid()) {
1134                        if (ERR.isLoggable(Level.FINE)) {
1135                            ERR.fine("Invalid file object: " + myFileObject); // NOI18N
1136
}
1137                        return NULL;
1138                    }
1139                    
1140                    parsedId = NULL;
1141                    if (ERR.isLoggable(Level.FINE)) ERR.fine("parsedId set to NULL for " + XMLDataObject.this); // NOI18N
1142
try {
1143                        in = myFileObject.getInputStream();
1144                    } catch (IOException ex) {
1145                        warning(ex, "I/O exception while openning xml."); //NOI18N
1146
return NULL; // cannot parse
1147
}
1148                    try {
1149
1150                        //
1151
// we use one shared parser instance, so we must protect
1152
// its integrity
1153
//
1154
synchronized (sharedParserImpl) {
1155
1156                            configureParser(parser, false, this);
1157                            parser.setContentHandler(this);
1158                            parser.setErrorHandler(this);
1159
1160                            InputSource input = new InputSource(url.toExternalForm());
1161                            input.setByteStream(in);
1162                            parser.parse (input);
1163                        }
1164                        if (ERR.isLoggable(Level.FINE)) {
1165                            ERR.fine("Parse finished for " + XMLDataObject.this); // NOI18N
1166
}
1167                    } catch (StopSaxException stopped) {
1168                        newID = parsedId;
1169                        ERR.fine("Parsing successfully stopped: " + parsedId + " for " + XMLDataObject.this); // NOI18N
1170
} catch (SAXException checkStop) {
1171                        // stop parsing anyway
1172
if (STOP.getMessage ().equals (checkStop.getMessage ())) {
1173                            newID = parsedId;
1174                            ERR.fine("Parsing stopped with STOP message: " + parsedId + " for " + XMLDataObject.this); // NOI18N
1175
} else {
1176                            String JavaDoc msg = "Thread:" + Thread.currentThread().getName(); //NOI18N
1177
ERR.warning("DocListener should not throw SAXException but STOP one.\n" + msg); //NOI18N
1178
ERR.log(Level.WARNING, null, checkStop);
1179                            Exception JavaDoc ex = checkStop.getException();
1180                            if (ex != null) {
1181                                ERR.log(Level.WARNING, null, ex);
1182                            }
1183                        }
1184                    } catch (FileNotFoundException ex) {
1185                        // thrown when there is a problem with URL for example
1186
ERR.log(Level.WARNING, null, ex);
1187                    } catch (IOException ex) {
1188                       // error while parsing for public id hide it.
1189
// somebody have deleted the file meanwhile, because I do not lock?
1190
ERR.log(Level.WARNING, null, ex);
1191                    } finally {
1192                        
1193                        // such small memory leak can complicate profiling a lot
1194
// on the other hand it might cause performance regression
1195
// guard it by dedicated property
1196
if (Boolean.getBoolean("netbeans.profile.memory")) { // NOI18N
1197
// dettach from shared impl, it is static!
1198
parser.setContentHandler(NullHandler.INSTANCE);
1199                            parser.setErrorHandler(NullHandler.INSTANCE);
1200                            try {
1201                                parser.setProperty("http://xml.org/sax/properties/lexical-handler", NullHandler.INSTANCE); //NOI18N
1202
} catch (SAXException ignoreIt) {
1203                            }
1204
1205                            try {
1206                                // Crimson requires it to release old properties and handlers
1207
parser.parse((InputSource)null);
1208                            } catch (Exception JavaDoc ignoreIt) {
1209                            }
1210                        }
1211                        
1212                        parser = null;
1213                    }
1214
1215                } finally {
1216                    try {
1217                        if (in != null) in.close();
1218                    } catch (IOException ex) {
1219                        ERR.log(Level.WARNING, null, ex);
1220                    }
1221                }
1222            
1223            }
1224            
1225            if (ignorePreviousId != null && newID.equals (ignorePreviousId)) {
1226                // no updates in lookup
1227
if (ERR.isLoggable(Level.FINE)) ERR.fine("No update to ID: " + ignorePreviousId + " for " + XMLDataObject.this); // NOI18N
1228
return newID;
1229            }
1230            
1231            // out of any synchronized blocks udpate the lookup
1232
// because it can call into unknown places via its
1233
// Environment.findForOne
1234
if (ERR.isLoggable(Level.FINE)) ERR.fine("New id: " + newID + " for " + XMLDataObject.this); // NOI18N
1235

1236            if (newID != null) {
1237                updateLookup (previousID, newID);
1238            }
1239            
1240            return newID;
1241        }
1242
1243        
1244        /** Updates the ID.
1245         */

1246        private Lookup updateLookup (String JavaDoc previousID, String JavaDoc id) {
1247            synchronized (this) {
1248                if (previousID != null && previousID.equals (id) && lookup != null) {
1249                    ERR.fine("No need to update lookup: " + id + " for " + XMLDataObject.this); // NOI18N
1250
return lookup;
1251                }
1252            }
1253            
1254            
1255            Lookup newLookup;
1256
1257            // no lock here, because createInfoLookup & findForOne can call
1258
// foreing code
1259
Info info = getRegisteredInfo (id);
1260            if (info != null) {
1261                // use info
1262
newLookup = createInfoLookup (XMLDataObject.this, info);
1263                if (ERR.isLoggable(Level.FINE)) ERR.fine("Lookup from info: " + newLookup + " for " + XMLDataObject.this); // NOI18N
1264
} else {
1265                // ask the environment for the lookups
1266
newLookup = Environment.findForOne (XMLDataObject.this);
1267                if (ERR.isLoggable(Level.FINE)) ERR.fine("Lookup from env: " + newLookup + " for " + XMLDataObject.this); // NOI18N
1268
if (newLookup == null) {
1269                    newLookup = Lookup.EMPTY;
1270                }
1271            }
1272            
1273            synchronized (this) {
1274                // just one update of lookup in this InfoParser
1275
Lookup.Result prevRes = result;
1276
1277                lookup = newLookup;
1278                if (ERR.isLoggable(Level.FINE)) ERR.fine("Shared lookup updated: " + lookup + " for " + XMLDataObject.this); // NOI18N
1279
result = lookup.lookupResult(Node.Cookie.class);
1280                result.addLookupListener (this);
1281
1282                if (prevRes != null) {
1283                    prevRes.removeLookupListener (this);
1284                    if (ERR.isLoggable(Level.FINE)) ERR.fine("Firing property change for " + XMLDataObject.this); // NOI18N
1285
XMLDataObject.this.firePropertyChange (DataObject.PROP_COOKIE, null, null);
1286                    if (ERR.isLoggable(Level.FINE)) ERR.fine("Firing done for " + XMLDataObject.this); // NOI18N
1287
}
1288                
1289                return newLookup;
1290            }
1291        }
1292
1293
1294        /*
1295         * We reuse the parser so it must be reconfigured prior every parsing task.
1296         */

1297        private void configureParser(XMLReader parser, boolean validation, LexicalHandler JavaDoc lex) {
1298
1299            try {
1300                parser.setFeature("http://xml.org/sax/features/validation", validation); //NOI18N
1301
} catch (SAXException sex) {
1302                ERR.fine("Warning: XML parser does not support validation feature."); //NOI18N
1303
}
1304
1305            try {
1306                parser.setProperty("http://xml.org/sax/properties/lexical-handler", lex); //NOI18N
1307
} catch (SAXException sex) {
1308                ERR.fine("Warning: XML parser does not support lexical-handler feature."); //NOI18N
1309
//throw new Error("");
1310
}
1311        }
1312
1313
1314        
1315        // ~~~~~~~~~~ ERROR REPORTING ~~~~~~~~~~~~~~~~~~~~~~
1316

1317        public void warning (Throwable JavaDoc ex) {
1318            warning(ex, null);
1319        }
1320
1321        public void warning (Throwable JavaDoc ex, String JavaDoc annotation) {
1322           ERR.log(Level.WARNING, annotation, ex);
1323        }
1324        
1325        // LexicalHandler
1326

1327        public void startDTD(String JavaDoc root, String JavaDoc pID, String JavaDoc sID) throws SAXException {
1328            parsedId = pID == null ? NULL : pID;
1329            ERR.fine("Parsed to " + parsedId); // NOI18N
1330
stop();
1331        }
1332
1333        public void endDTD() throws SAXException {
1334            stop();
1335        }
1336
1337        public void startEntity(String JavaDoc name) throws SAXException {
1338        }
1339
1340        public void endEntity(String JavaDoc name) throws SAXException {
1341        }
1342
1343        public void startCDATA() throws SAXException {
1344        }
1345
1346        public void endCDATA() throws SAXException {
1347        }
1348
1349        public void comment(char[] ch, int start, int length) throws SAXException {
1350        }
1351
1352        // redefine DefaultHandler
1353

1354        //!!! should we stop on error?
1355
public void error(final SAXParseException p1) throws org.xml.sax.SAXException JavaDoc {
1356            stop();
1357        }
1358
1359        public void fatalError(final SAXParseException p1) throws org.xml.sax.SAXException JavaDoc {
1360            stop();
1361        }
1362
1363        public void endDocument() throws SAXException {
1364            stop();
1365        }
1366
1367        public void startElement(String JavaDoc uri, String JavaDoc lName, String JavaDoc qName, Attributes atts) throws SAXException {
1368            // no DTD present
1369
stop();
1370        }
1371
1372        private void stop() throws SAXException {
1373            throw STOP;
1374        }
1375
1376        
1377        //~~~~~~~~~~~~~~~~~~ FS LISTENER ~~~~~~~~~~~~~~~~~
1378
//listening at parent folder
1379

1380        public void fileFolderCreated (FileEvent fe) {
1381            // not interesting
1382
}
1383
1384        public void fileDataCreated (FileEvent fe) {
1385// FileObject fo = fe.getFile();
1386
// fileCreated(fo);
1387
}
1388
1389        private void fileCreated(FileObject fo) {
1390// if (
1391
// fo.getName ().equals (getPrimaryFile ().getName ())
1392
// ) {
1393
// // new info file created => force it to be reparsed
1394
// resolveInfo();
1395
// }
1396
}
1397
1398        /** Fired when a file is changed.
1399        * @param fe the event describing context where action has taken place
1400        */

1401        public void fileChanged (FileEvent fe) {
1402            if (getPrimaryFile ().equals (fe.getFile ())) {
1403                // the main file changed => invalidate DOM document
1404
//resolveInfo (getPrimaryFile ()); //reparse info again
1405
clearDocument ();
1406                String JavaDoc prevId = parsedId;
1407                parsedId = null;
1408                ERR.fine("cleared parsedId"); // NOI18N
1409
// parse update only if the ID is different
1410
waitFinished (prevId);
1411            }
1412        }
1413
1414        public void fileDeleted (FileEvent fe) {
1415        }
1416
1417        public void fileRenamed (FileRenameEvent fe) {
1418// // the same behaviour as when the file is deleted
1419
// fileDeleted (fe);
1420
// // and new created
1421
// fileCreated(fe.getFile());
1422
}
1423
1424        public void fileAttributeChanged (FileAttributeEvent fe) {
1425            // not interested in
1426
}
1427        
1428        /** A change in lookup.
1429         */

1430        public void resultChanged(LookupEvent lookupEvent) {
1431            XMLDataObject.this.firePropertyChange (DataObject.PROP_COOKIE, null, null);
1432            
1433            Node n = XMLDataObject.this.getNodeDelegateOrNull ();
1434            if (n instanceof XMLNode) {
1435                ((XMLNode)n).update ();
1436            }
1437        }
1438        
1439    } // end of InfoParser
1440

1441
1442    /** Avoid Internet connections */
1443    private static class EmptyEntityResolver implements EntityResolver {
1444        EmptyEntityResolver() {}
1445        public InputSource resolveEntity(String JavaDoc publicId, String JavaDoc systemID) {
1446            InputSource ret = new InputSource(new StringReader("")); //??? we should tolerate file: and nbfs: // NOI18N
1447
ret.setSystemId("StringReader"); //NOI18N
1448
return ret;
1449        }
1450    }
1451    
1452    
1453    
1454    // ~~~~~~~~~~~~~~~~~~~~~~~~~~ private Loader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1455

1456        
1457    /** The DataLoader for XmlDataObjects.
1458     */

1459    static final class Loader extends MultiFileLoader {
1460        static final long serialVersionUID =3917883920409453930L;
1461        /** Creates a new XMLDataLoader */
1462        public Loader () {
1463        super ("org.openide.loaders.XMLDataObject"); //!!! so the relation loader data object is fixed // NOI18N
1464
//super (XMLDataObject.class); // nothing like looks loader can be constructed
1465
} // can it produce subclasses?
1466

1467        protected String JavaDoc actionsContext () {
1468            return "Loaders/text/xml/Actions"; // NOI18N
1469
}
1470        
1471        /** Get the default display name of this loader.
1472        * @return default display name
1473        */

1474        protected String JavaDoc defaultDisplayName () {
1475            return NbBundle.getMessage (XMLDataObject.class, "PROP_XmlLoader_Name");
1476        }
1477        
1478        /** For a given file finds a primary file.
1479        * @param fo the file to find primary file for
1480        *
1481        * @return the primary file for the file or null if the file is not
1482        * recognized by this loader
1483        */

1484        protected FileObject findPrimaryFile (FileObject fo) {
1485            String JavaDoc mime = fo.getMIMEType ();
1486            if (mime.endsWith("/xml") || mime.endsWith("+xml")) { // NOI18N
1487
return fo;
1488            }
1489            // not recognized
1490
return null;
1491        }
1492        
1493        /** Creates the right data object for given primary file.
1494        * It is guaranteed that the provided file is realy primary file
1495        * returned from the method findPrimaryFile.
1496        *
1497        * @param primaryFile the primary file
1498        * @return the data object for this file
1499        * @exception DataObjectExistsException if the primary file already has data object
1500        */

1501        protected MultiDataObject createMultiObject (FileObject primaryFile)
1502        throws DataObjectExistsException {
1503            return new XMLDataObject (primaryFile, this);
1504        }
1505
1506        /** Creates the right primary entry for given primary file.
1507        *
1508        * @param primaryFile primary file recognized by this loader
1509        * @return primary entry for that file
1510        */

1511        protected MultiDataObject.Entry createPrimaryEntry (MultiDataObject obj, FileObject primaryFile) {
1512            return new FileEntry (obj, primaryFile);
1513        }
1514
1515        /** Creates right secondary entry for given file. The file is said to
1516        * belong to an object created by this loader.
1517        *
1518        * @param secondaryFile secondary file for which we want to create entry
1519        * @return the entry
1520        */

1521        protected MultiDataObject.Entry createSecondaryEntry (MultiDataObject obj, FileObject secondaryFile) {
1522            // JST: We do not have secondary entries anymore, but it probably does not matter...
1523
return new FileEntry (obj, secondaryFile);
1524        }
1525    }
1526
1527    // ~~~~~~~~~~~~~~~~~~~~~~ extension support via info ~~~~~~~~~~~~~~~~~~~~~~~~
1528

1529    // i would like to throw it away sometimes in future and replace it by better one
1530

1531    /** This class has to be implemented by all processors in the
1532    * xmlinfo file. It is cookie, so after parsing such class is instantiated
1533    * and put into data objects cookie set.
1534    *
1535    * @deprecated use lookup
1536    */

1537    @Deprecated JavaDoc
1538    public static interface Processor extends Node.Cookie {
1539        /** When the XMLDataObject creates new instance of the processor,
1540        * it uses this method to attach the processor to the data object.
1541        *
1542        * @param xmlDO XMLDataObject
1543        */

1544        public void attachTo (XMLDataObject xmlDO);
1545    }
1546    
1547    
1548    /** @deprecated use Lookup
1549     * Representation of xmlinfo file holding container of Processors.
1550     */

1551    @Deprecated JavaDoc
1552    public static final class Info implements Cloneable JavaDoc {
1553        List<Class JavaDoc<?>> processors;
1554        String JavaDoc iconBase;
1555
1556        /** Create info */
1557        public Info () {
1558            processors = new ArrayList<Class JavaDoc<?>> ();
1559            iconBase = null;
1560        }
1561
1562        public Object JavaDoc clone () {
1563            Info ii = new Info();
1564            for (Class JavaDoc<?> proc: processors) {
1565                ii.processors.add (proc);
1566            }
1567            ii.iconBase = iconBase;
1568            return ii;
1569        }
1570
1571        /** Add processor class to info.
1572        * The class should be public and either implement the Processor
1573        * interface or should
1574        * have public constructor with one argument (DataObject or XMLDataObject).
1575        *
1576        * @param proc the class to add to this info
1577        * @exception IllegalArgumentException if the class does not seem to be valid
1578        */

1579        public synchronized void addProcessorClass(Class JavaDoc<?> proc) {
1580            if (!Processor.class.isAssignableFrom (proc)) {
1581                Constructor[] arr = proc.getConstructors();
1582                for (int i = 0; i < arr.length; i++) {
1583                    Class JavaDoc[] params = arr[i].getParameterTypes();
1584                    if (params.length == 1) {
1585                        if (
1586                            params[0] == DataObject.class ||
1587                            params[0] == XMLDataObject.class
1588                        ) {
1589                            arr = null;
1590                            break;
1591                        }
1592                    }
1593                }
1594                
1595                if (arr != null) {
1596                    // no suitable constructor
1597
throw new IllegalArgumentException JavaDoc();
1598                }
1599            }
1600            
1601            processors.add (proc);
1602        }
1603
1604        /** Remove processor class from info.
1605         * @return true if removed
1606         */

1607        public boolean removeProcessorClass(Class JavaDoc<?> proc) {
1608            return processors.remove (proc);
1609        }
1610
1611        public Iterator<Class JavaDoc<?>> processorClasses() {
1612            return processors.iterator();
1613        }
1614
1615        /** Set icon base */
1616        public void setIconBase (String JavaDoc base) {
1617            iconBase = base;
1618        }
1619
1620        /** @return icon base */
1621        public String JavaDoc getIconBase () {
1622            return iconBase;
1623        }
1624
1625        /** Write specified info to writer */
1626        public void write (Writer writer) throws IOException {
1627            throw new IOException ("Not supported anymore"); // NOI18N
1628
}
1629        
1630        public boolean equals (Object JavaDoc obj) {
1631            if (obj == null) return false;
1632            if (obj instanceof Info == false) return false;
1633            
1634            Info i = (Info) obj;
1635            
1636            return ((iconBase != null && iconBase.equals(i.iconBase)) || (i.iconBase == iconBase))
1637                    && processors.equals(i.processors);
1638        }
1639    } // end of inner class Info
1640

1641    
1642    /** A method for backward compatibility to create a lookup from data object and info
1643     * @param obj xml data object
1644     * @param info the info that should be associated
1645     */

1646    static Lookup createInfoLookup (XMLDataObject obj, Info info) {
1647        return new InfoLkp (obj, info);
1648    }
1649
1650    
1651    /** A backward compatibility class that converts the content of
1652     * an Info object into a Lookup class.
1653     */

1654    private static final class InfoLkp extends AbstractLookup {
1655        public final Info info;
1656        
1657        public InfoLkp (XMLDataObject obj, Info info) {
1658            this.info = info;
1659            
1660            Iterator<Class JavaDoc<?>> it = info.processorClasses ();
1661            ArrayList<InfoPair> arr = new ArrayList<InfoPair> (info.processors.size ());
1662            while (it.hasNext ()) {
1663                Class JavaDoc<?> c = it.next ();
1664                arr.add (new InfoPair (obj, c));
1665            }
1666            
1667            setPairs (arr);
1668        }
1669        
1670        /** A pair that receives a class and can create its instance either
1671         * using default constructor or by passing data object into one
1672         * argument constructor.
1673         */

1674        private static final class InfoPair extends AbstractLookup.Pair {
1675            /** the class to use or null if object has already been created */
1676            private Class JavaDoc<?> clazz;
1677            /** XMLDataObject associated or object created */
1678            private Object JavaDoc obj;
1679            
1680            /** For use by subclasses. */
1681            protected InfoPair (XMLDataObject obj, Class JavaDoc<?> c) {
1682                this.obj = obj;
1683                this.clazz = c;
1684            }
1685
1686            /** Tests whether this item can produce object
1687            * of class c.
1688            */

1689            protected boolean instanceOf (Class JavaDoc c) {
1690                Class JavaDoc<?> temp = clazz;
1691                if (temp == null) {
1692                    return c.isInstance (obj);
1693                } else {
1694                    return c.isAssignableFrom (temp);
1695                }
1696            }
1697
1698            /** Method that can test whether an instance of a class has been created
1699             * by this item.
1700             *
1701             * @param obj the instance
1702             * @return if the item has already create an instance and it is the same
1703             * as obj.
1704             */

1705            protected boolean creatorOf (Object JavaDoc obj) {
1706                return this.obj == obj;
1707            }
1708
1709            /** The class of the result item.
1710             * @return the instance of the object.
1711             */

1712            public synchronized Object JavaDoc getInstance () {
1713                if (clazz == null) {
1714                    // already created an object
1715
return obj;
1716                }
1717                
1718                // after this method the obj or null will contain the created object
1719
// instead of reference to XMLDataObject
1720
XMLDataObject xmlDataObject = (XMLDataObject)obj;
1721                obj = null;
1722                
1723                // the clazz will be null to signal, that an instance
1724
// of object has been created
1725
Class JavaDoc next = clazz;
1726                clazz = null;
1727
1728                try {
1729                    if (Processor.class.isAssignableFrom (next)) {
1730                        // the class implements Processor interface, so use
1731
// default constructor to construct instance
1732
obj = next.newInstance ();
1733                        Processor proc = (Processor) obj;
1734                        proc.attachTo (xmlDataObject);
1735                        return obj;
1736                    } else {
1737                        // does not implement processor, try to search
1738
// for constructor with one argument of DataObject or
1739
// XMLDataObject
1740

1741                        Constructor[] arr = next.getConstructors();
1742                        for (int i = 0; i < arr.length; i++) {
1743                            Class JavaDoc[] params = arr[i].getParameterTypes();
1744                            if (params.length == 1) {
1745                                if (
1746                                    params[0] == DataObject.class ||
1747                                    params[0] == XMLDataObject.class
1748                                ) {
1749                                    obj = arr[i].newInstance(
1750                                        new Object JavaDoc[] { xmlDataObject }
1751                                    );
1752                                    return obj;
1753                                }
1754                            }
1755                        }
1756                    }
1757                    throw new InternalError JavaDoc ("XMLDataObject processor class " + next + " invalid"); // NOI18N
1758
} catch (InvocationTargetException e) {
1759                    xmlDataObject.notifyEx (e);
1760                } catch (InstantiationException JavaDoc e) {
1761                    xmlDataObject.notifyEx(e);
1762                } catch (IllegalAccessException JavaDoc e) {
1763                    xmlDataObject.notifyEx(e);
1764                }
1765                
1766                return obj;
1767            }
1768
1769            /** The class of the result item.
1770             * @return the class of the item
1771             */

1772            public Class JavaDoc getType () {
1773                Class JavaDoc temp = clazz;
1774                return temp != null ? temp : obj.getClass ();
1775            }
1776
1777            /** A persistent indentifier of the item. Can be stored and use
1778             * in next run of the system.
1779             *
1780             * @return a string id of the item
1781             */

1782            public String JavaDoc getId () {
1783                return "Info[" + getType ().getName (); // NOI18N
1784
}
1785            
1786            /** The best display name is probably the name of type...
1787             */

1788            public String JavaDoc getDisplayName () {
1789                return getType ().getName ();
1790            }
1791        }
1792    }
1793    
1794
1795    /** Computes correct node for given XMLDataObject.
1796     */

1797    private Node findNode () {
1798        Node n = (Node)getIP ().lookupCookie (Node.class);
1799
1800        if (n == null) {
1801            return new PlainDataNode();
1802        } else {
1803            return n;
1804        }
1805    }
1806    
1807    private final class PlainDataNode extends DataNode {
1808        public PlainDataNode() {
1809            super(XMLDataObject.this, Children.LEAF);
1810            setIconBaseWithExtension("org/openide/loaders/xmlObject.gif"); // NOI18N
1811
}
1812        public Action JavaDoc getPreferredAction() {
1813            return SystemAction.get(OpenAction.class);
1814        }
1815    }
1816        
1817    
1818    /** Node that delegates either to data node or to a node provided by
1819     * the data object itself.
1820     */

1821    private final class XMLNode extends FilterNode {
1822        public XMLNode (XMLDataObject obj) {
1823            this (obj.findNode ());
1824        }
1825        private XMLNode (Node del) {
1826            super (del, new FilterNode.Children (del));
1827            //setShortDescription("XML FILE");
1828
}
1829        private void update () {
1830            changeOriginal (XMLDataObject.this.findNode (), true);
1831        }
1832        
1833    }
1834    
1835    /** A special delegator that adds InstanceCookie.Of to objects that miss it
1836     */

1837    private static class ICDel extends Object JavaDoc implements InstanceCookie.Of {
1838        /** object we belong to
1839         */

1840        private XMLDataObject obj;
1841        /** cookie we delegate to */
1842        private InstanceCookie ic;
1843
1844        public ICDel (XMLDataObject obj, InstanceCookie ic) {
1845            this.obj = obj;
1846            this.ic = ic;
1847        }
1848
1849
1850        public String JavaDoc instanceName () {
1851            return ic.instanceName ();
1852        }
1853
1854        public Class JavaDoc<?> instanceClass ()
1855        throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
1856            return ic.instanceClass ();
1857        }
1858
1859        public Object JavaDoc instanceCreate ()
1860        throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
1861            return ic.instanceCreate ();
1862        }
1863
1864        public boolean instanceOf (Class JavaDoc cls2) {
1865            if (ic instanceof InstanceCookie.Of) {
1866                return ((InstanceCookie.Of) ic).instanceOf (cls2);
1867            } else {
1868                try {
1869                    return cls2.isAssignableFrom (instanceClass ());
1870                } catch (IOException ioe) {
1871                    // ignore exception
1872
return false;
1873                } catch (ClassNotFoundException JavaDoc cnfe) {
1874                    // ignore exception
1875
return false;
1876                }
1877            }
1878        }
1879        
1880        public int hashCode () {
1881            return 2 * obj.hashCode () + ic.hashCode ();
1882        }
1883        
1884        public boolean equals (Object JavaDoc obj) {
1885            if (obj instanceof ICDel) {
1886                ICDel d = (ICDel)obj;
1887                return d.obj == obj && d.ic == ic;
1888            }
1889            return false;
1890        }
1891    } // end of ICDel
1892

1893    /** Delegating DOM document that provides fast implementation of
1894     * getDocumentType and getPublicID methods.
1895     */

1896    private final class DelDoc implements InvocationHandler {
1897        
1898        private Reference<Document JavaDoc> xmlDocument;
1899        private final Document JavaDoc proxyDocument;
1900        
1901        DelDoc() {
1902            proxyDocument = (Document JavaDoc)Proxy.newProxyInstance(
1903                DelDoc.class.getClassLoader(), new Class JavaDoc[] {Document JavaDoc.class}, this);
1904        }
1905
1906        /** Creates w3c's document for the xml file. Either returns cached reference
1907        * or parses the file and creates new document.
1908        *
1909        * @param force really create the document if it does not exists yet?
1910        * @return the parsed document or null if not forced
1911        */

1912        private final Document JavaDoc getDocumentImpl (boolean force) {
1913            synchronized (this) {
1914                Document JavaDoc doc = xmlDocument == null ? null : xmlDocument.get ();
1915                if (doc != null) {
1916                    return doc;
1917                }
1918                
1919                if (!force) {
1920                    return null;
1921                }
1922
1923                status = STATUS_OK;
1924                try {
1925                    Document JavaDoc d = parsePrimaryFile ();
1926                    xmlDocument = new SoftReference<Document JavaDoc> (d);
1927                    return d;
1928                } catch (SAXException e) {
1929                    ERR.log(Level.WARNING, null, e);
1930                } catch (IOException e) {
1931                    ERR.log(Level.WARNING, null, e);
1932                }
1933                
1934                status = STATUS_ERROR;
1935                Document JavaDoc d = XMLUtil.createDocument("brokenDocument", null, null, null); // NOI18N
1936

1937                xmlDocument = new SoftReference<Document JavaDoc> (d);
1938                
1939                // fire property change, because the document is errornous
1940
firePropertyChange (PROP_DOCUMENT, null, null);
1941                
1942                return d;
1943            }
1944        }
1945        
1946        /**
1947         * Get the externally usable, lazy document.
1948         * Delegates everything to the parsed document on disk (parsing as necessary),
1949         * except that getDoctype().getPublicId() is specially implemented so as to
1950         * not require loading the whole document.
1951         */

1952        public Document JavaDoc getProxyDocument() {
1953            return proxyDocument;
1954        }
1955        
1956        public Object JavaDoc invoke(Object JavaDoc proxy, Method method, Object JavaDoc[] args) throws Throwable JavaDoc {
1957            if (method.getName().equals("getDoctype") && args == null) { // NOI18N
1958
return Proxy.newProxyInstance(DelDoc.class.getClassLoader(), new Class JavaDoc[] {DocumentType JavaDoc.class}, this);
1959            } else if (method.getName().equals("getPublicId") && args == null) { // NOI18N
1960
Document JavaDoc d = getDocumentImpl(false);
1961                if (d != null) {
1962                    DocumentType JavaDoc doctype = d.getDoctype();
1963                    return doctype == null ? null : doctype.getPublicId();
1964                } else {
1965                    return getIP().getPublicId();
1966                }
1967            } else {
1968                return method.invoke(getDocumentImpl(true), args);
1969            }
1970        }
1971        
1972    }
1973    
1974}
1975
Popular Tags