alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.loaders;

import java.net.URL;
import java.io.*;
import java.util.*;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.*;
import java.beans.*;

import javax.xml.parsers.*;


import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.*;
import org.w3c.dom.*;

import org.openide.*;
import org.openide.actions.*;
import org.openide.cookies.*;
import org.openide.filesystems.*;
import org.openide.loaders.*;
import org.openide.text.*;
import org.openide.util.*;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.actions.SystemAction;
import org.openide.xml.*;
import org.openide.nodes.Node;
import org.openide.nodes.Children;
import org.openide.nodes.CookieSet;
import org.openide.windows.CloneableOpenSupport;
import org.openide.nodes.FilterNode;

/** 
 * Object that provides main functionality for xml documents.
 * These objects are recognized by the xml extension and
 * text/xml MIME type. 
 * 

* It is declaratively extensible by an {@link Environment}. * The Environment is assigned to document instances using a provider * registered by DOCTYPE's public ID in the system filesystem under * xml/lookups/{Transformed-DOCTYPE} where the DOCTYPE transformation * is the same as that defined for {@link EntityCatalog} registrations. * * @see XMLUtil * @see EntityCatalog * * @author Libor Kramolis, Jaroslav Tulach, Petr Kuzel */ public class XMLDataObject extends MultiDataObject { /** generated Serialized Version UID */ static final long serialVersionUID = 8757854986453256578L; /** Public ID of xmlinfo dtd. * @deprecated replaced with Lookup */ public static final String XMLINFO_DTD_PUBLIC_ID_FORTE = "-//Forte for Java//DTD xmlinfo//EN"; // NOI18N /** @deprecated replaced with Lookup */ public static final String XMLINFO_DTD_PUBLIC_ID = "-//NetBeans IDE//DTD xmlinfo//EN"; // NOI18N /** Mime type of XML documents. */ public static final String MIME = "text/xml"; //NOI18N //public static final String MIME2 = "application/xml"; //NOI18N /** PROP_DOCUMENT not parsed yet. Constant for getStatus method. */ public static final int STATUS_NOT = 0; /** PROP_DOCUMENT parsed ok. Constant for getStatus method. */ public static final int STATUS_OK = 1; /** PROP_DOCUMENT parsed with warnings. Constant for getStatus method. */ public static final int STATUS_WARNING = 2; /** PROP_DOCUMENT parsed with errors. Constant for getStatus method. */ public static final int STATUS_ERROR = 3; /** property name of DOM document property */ public static final String PROP_DOCUMENT = "document"; //??? it is not bound well // NOI18N /** property name of info property * @deprecated info is not supported anymore. Replaced with lookup. */ public static final String PROP_INFO = "info"; // NOI18N /** Default XML parser error handler */ private static ErrorPrinter errorHandler = new ErrorPrinter(); /** * Chain of resolvers contaning all EntityResolvers registred by a user. */ private static XMLEntityResolverChain chainingEntityResolver; /** map of DTD publicID => Info. */ private static HashMap infos = new HashMap(); // the lock can be seamlesly shared by all instances private static Object emgrLock = new Object (); // // Instance variables // /** the XML document we delegate to */ private DelDoc doc; /** the result of parsing */ private int status; //??? why it is not a bound property? // it if often out-of date (e.g. garbage collection) /** @deprecated EditorCookie provided by subclass support * need to prevail build in cookies. */ private EditorCookie editor = null; /** * Task body triggered by file change (primaryFile() or xmlinfo) parsing document * for extension (info) assigment information (xmlinfo or public id) */ private InfoParser infoParser; /* Lazy initialized. For logging and debugging. */ private ErrorManager err; /** * Create new XMLDataObject. It is usually called by a loader. * A user can get existing XMLDataObject by calling {@link DataObject#find(FileObject) * DataObject.find(FileObject f)} instead. * * @param fo the primary file object, never null * @param loader loader of this data object, never null * */ public XMLDataObject (FileObject fo, MultiFileLoader loader) throws DataObjectExistsException { super (fo, loader); fo.addFileChangeListener (FileUtil.weakFileChangeListener (getIP (), fo)); status = STATUS_NOT; // register provided cookies // EditorCookie must be for back compatability consulted with subclasses // // In new model subclasses should directly provide its CookieSet.Factory that // uses last prevails order instead of old CookieSet first prevails order. // It completely prevails over this factory :-) CookieSet.Factory factory = new CookieSet.Factory() { public Node.Cookie createCookie(Class klass) { if (klass.isAssignableFrom(EditorCookie.class) || klass.isAssignableFrom(OpenCookie.class) || klass.isAssignableFrom(CloseCookie.class) || klass.isAssignableFrom(PrintCookie.class) ) { if (editor == null) editor = createEditorCookie(); // the first pass if (editor == null) return null; //??? gc unfriendly return klass.isAssignableFrom(editor.getClass()) ? editor : null; } else { return null; } } }; CookieSet cookies = getCookieSet(); // EditorCookie.class must be synchronized with // XMLEditor.Env->findCloneableOpenSupport cookies.add(EditorCookie.class, factory); cookies.add(OpenCookie.class, factory); cookies.add(CloseCookie.class, factory); cookies.add(PrintCookie.class, factory); // set info for this file //getIP ().resolveInfo (); #16045 } /** Getter for info parser. Initializes the infoparser in "lazy" way so it is accessble even before * the constructor finishes. */ private final InfoParser getIP () { synchronized (emgrLock) { if (infoParser == null) { infoParser = new InfoParser (); } } return infoParser; } /** If the Info associated with this data object (if any) provides * a subclass of Node, then this object is created to represent the * XML data object, otherwise DataNode is created. * * @return the node representation for this data object * @see DataNode */ protected Node createNodeDelegate () { //??? what about interaction with Looks XMLNode xn = new XMLNode (this); // netbeans.core.nodes.description xn.setShortDescription (NbBundle.getMessage ( XMLDataObject.class, "HINT_XMLDataObject")); // NOI18N return xn; } /** Called when the info file is parsed and the icon should change. * @param res resource for the icon * @deprecated it is better to listen on properties */ protected void updateIconBase (String res) { //??? we could add default behaviour, taking status into account } /* * Wait until background parsing terminates to avoid concurent file access. * It should terminate very early if just running, we can wait for it. */ protected void handleDelete() throws IOException { getIP ().waitFinished(); // too late wait for finnish super.handleDelete(); } public HelpCtx getHelpCtx () { // help for fix #23528, objects represents 'settings' nodes in Options dialog // returns DEFAULT_HELP for next processing try { if (getPrimaryFile ().getFileSystem ().isDefault ()) { if (getCookie (InstanceCookie.class)!=null) { return HelpCtx.DEFAULT_HELP; } } } catch (FileStateInvalidException fsie) { // cannot determine type of this file object ==> return help id as normal } return new HelpCtx (XMLDataObject.class); } /** * Cookies from assigned Environment are not placed into * protected CookieSet and can be obtained only by invoking this method. *

* Cookie order for Info environments are handled consistently with * CookieSet i.e. FIFO. * @return a cookie (instanceof cls) that has been found in info or * super.getCookie(cls). */ public Node.Cookie getCookie (Class cls) { getIP ().waitFinished(); Node.Cookie cake = (Node.Cookie)getIP ().lookupCookie (cls); if (cake instanceof InstanceCookie) { cake = ofCookie ((InstanceCookie)cake, cls); } if (cake == null) { cake = super.getCookie (cls); } return cake; } /** Special support of InstanceCookie.Of. If the Info class * provides InstanceCookie but not IC.Of, we add the extra interface to * this data object. * * @param ic instance cookie * @param cls constraining class * @return instance of InstanceCookie.Of */ private InstanceCookie ofCookie (InstanceCookie ic, Class cls) { if (ic instanceof InstanceCookie.Of) { return ic; } else if (! cls.isAssignableFrom (ICDel.class)) { // Someone was looking for, and a processor etc. was // providing, some specialization which ICDel cannot // provide. Return the real implementation and forget // about making this a IC.Of. return ic; } else { ICDel d = new ICDel (this, ic); return d; } } private void notifyEx(Exception e) { ErrorManager emgr = ErrorManager.getDefault(); emgr.annotate(e, "Cannot resolve following class in xmlinfo."); // NOI18N emgr.notify(e); } /** Allows subclasses to provide their own editor cookie. * @return an editor cookie to be used as a result of getCookie(EditorCookie.class) * * @deprecated CookieSet factory should be used by subclasses instead. */ protected EditorCookie createEditorCookie () { return new XMLEditorSupport (this); } // Vertical CookieManager private final void addSaveCookie (SaveCookie save) { getCookieSet ().add (save); } private final void removeSaveCookie (SaveCookie save) { getCookieSet ().remove (save); } //??? we ahould add it into class comment to make it public // or should we introduce second layer XMLDataObject extending this one // and having documented this functionality (we cannot because of // so this huge DataObject will survive createEditorCookie()) /* * Really simple implementation of OpenCookie, EditorCookie, PrintCookie, * CloseCookie and managing SaveCookie. */ private static class XMLEditorSupport extends DataEditorSupport implements OpenCookie, EditorCookie.Observable, PrintCookie, CloseCookie { public XMLEditorSupport (XMLDataObject obj) { super (obj, new XMLEditorEnv (obj)); setMIMEType ("text/xml"); // NOI18N } class Save implements SaveCookie { public void save () throws IOException { saveDocument (); getDataObject ().setModified (false); } } protected boolean notifyModified () { if (! super.notifyModified ()) { return false; } if (getDataObject ().getCookie (SaveCookie.class) == null) { ((XMLDataObject) getDataObject ()).addSaveCookie (new Save ()); getDataObject ().setModified (true); } return true; } protected void notifyUnmodified () { super.notifyUnmodified (); SaveCookie save = (SaveCookie) getDataObject ().getCookie (SaveCookie.class); if (save != null) { ((XMLDataObject) getDataObject ()).removeSaveCookie (save); getDataObject ().setModified (false); } } //!!! it also stays for SaveCookie however does not understand // encoding declared in XML header => need to be rewritten. private static class XMLEditorEnv extends DataEditorSupport.Env { private static final long serialVersionUID = 6593415381104273008L; public XMLEditorEnv (DataObject obj) { super (obj); } protected FileObject getFile () { return getDataObject ().getPrimaryFile (); } protected FileLock takeLock () throws IOException { return ((XMLDataObject) getDataObject ()).getPrimaryEntry ().takeLock (); } public CloneableOpenSupport findCloneableOpenSupport () { // must be sync with cookies.add(EditorCookie.class, factory); // #12938 XML files do not persist in Source editor return (CloneableOpenSupport) getDataObject ().getCookie (EditorCookie.class); } } } /** Creates w3c's document for the xml file. Either returns cached reference * or parses the file and creates new document. * * @return the parsed document * @exception SAXException if there is a parsing error * @exception IOException if there is an I/O error */ public final Document getDocument () throws IOException, SAXException { emgr().log ("getDocument"); synchronized (this) { DelDoc d = doc; if (d == null) { d = new DelDoc (); doc = d; } return d.getProxyDocument(); } } /** Clears the document. Called when the document file is changed. */ final void clearDocument () { emgr().log ("clearDocument"); //err.notify (ErrorManager.INFORMATIONAL, new Throwable ("stack dump")); doc = null; firePropertyChange (PROP_DOCUMENT, null, null); } /** * @return one of STATUS_XXX constants representing PROP_DOCUMENT state. */ public final int getStatus () { return status; } /** @deprecated not used anymore * @return null */ public final Info getInfo () { return null; } /** @deprecated does not do anything useful */ public final synchronized void setInfo (Info ii) throws IOException { } /* JST: Commented out, we are disabling support for Infos * private final void setInfoImpl (Info ii) { if (info == ii) return; if ((info != null) && info.equals (ii)) return; // update properties and caches Info prevInfo = info; info = ii; if (info != null) { cachedCookies = null; updateIconBase (info.getIconBase ()); //??? the fire bellow shoud do it, why explicitly } firePropertyChange (PROP_INFO, prevInfo, info); } private void writeInfo () throws IOException { if (info == null) return; final FileObject primary = getPrimaryFile(); final FileObject parent = primary.getParent(); // a folder final org.openide.filesystems.FileSystem FS = parent.getFileSystem(); FS.runAtomicAction (new org.openide.filesystems.FileSystem.AtomicAction () { public void run () throws IOException { FileLock lock = null; OutputStream os = null; FileObject infoFO = FS.find (parent.getName(), primary.getName(), Loader.XMLINFO_EXT); if (infoFO == null) infoFO = parent.createData (primary.getName(), Loader.XMLINFO_EXT); try { lock = infoFO.lock (); os = infoFO.getOutputStream (lock); PrintWriter writer = new PrintWriter (new BufferedOutputStream(os)); info.write (writer); writer.close(); } finally { if (os != null) os.close (); if (lock != null) lock.releaseLock (); } } }); } */ /** Parses the primary file of this data object. * and provide different implementation. * * @return the document in the primary file * @exception IOException if error during parsing occures */ final Document parsePrimaryFile () throws IOException, SAXException { emgr().log ("parsePrimaryFile"); String loc = getPrimaryFile().getURL().toExternalForm(); try { return XMLUtil.parse(new InputSource(loc), false, /* #36295 */true, errorHandler, getSystemResolver()); } catch (IOException e) { // Perhaps this document was not on a mounted filesystem. // Try again with an input stream - no relative URLs will work, but this // is extremely unlikely to matter. Cf. #36340. InputStream is = getPrimaryFile().getInputStream(); try { return XMLUtil.parse(new InputSource(is), false, true, errorHandler, getSystemResolver()); } finally { is.close(); } } } /** Return ErrorManager for this instance. */ private ErrorManager emgr() { synchronized (emgrLock) { if (err == null) { err = ErrorManager.getDefault ().getInstance( "org.openide.loaders.XMLDataObject[" + getPrimaryFile().getPath() + "]" // NOI18N ); } } return err; } // ~~~~~~~~~~~~~~~~~~~~ Start of Utilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~ To be replaced by XMLUtil ~~~~~~~~~~~~~~~~~~~~~~ /** Provides access to internal XML parser. * This method takes URL. After successful finish the * document tree is returned. Used non validating parser. * * @param url the url to read the file from * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead * setting null error handler and validation to false. */ public static Document parse (URL url) throws IOException, SAXException { return parse (url, errorHandler, false); } /** Provides access to internal XML parser. * This method takes URL. After successful finish the * document tree is returned. Used non validating parser. * * @param url the url to read the file from * @param validate if true validating parser is used * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead * setting null handler. */ public static Document parse (URL url, boolean validate) throws IOException, SAXException { return parse (url, errorHandler, validate); } /** Provides access to internal XML parser. * This method takes URL. After successful finish the * document tree is returned. * * @param url the url to read the file from * @param eh error handler to notify about exception * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead * setting validation to false. */ public static Document parse (URL url, ErrorHandler eh) throws IOException, SAXException { return parse (url, eh, false); } /** Factory a DocumentBuilder and let it create a org.w3c.dom.Document * This method takes URL. After successful finish the * document tree is returned. * A parser producing the Document has * set entity resolver to system entity resolver chain. * * @param url the url to read the file from * @param eh error handler to notify about exception * @param validate if true validating parser is used * @throws SAXException annotated if thrown due to configuration problem * @throws FactoryConfigurationError * @return org.w3c.dom.Document * @deprecated Use {@link XMLUtil#parse(InputSource, boolean, boolean, ErrorHandler, EntityResolver) XMLUtil} instead. */ public static Document parse (URL url, ErrorHandler eh, boolean validate) throws IOException, SAXException { DocumentBuilder builder = XMLDataObjectImpl.makeBuilder(validate); builder.setErrorHandler(eh); builder.setEntityResolver(getChainingEntityResolver()); return builder.parse (new InputSource(url.toExternalForm())); } /** Creates SAX parse that can be used to parse XML files. * @return sax parser * @deprecated Use {@link XMLUtil#createXMLReader() XMLUtil} instead. * It will create a SAX XMLReader that is SAX Parser replacement. * You will have to replace DocumentHandler by ContentHandler * besause XMLReader accepts just ContentHandler. *

Alternatively if not interested in new callbacks defined by * SAX 2.0 you can wrap returned XMLReader into XMLReaderAdapter * that implements Parser. */ public static Parser createParser () { return createParser (false); } /** Factory SAX parser that can be used to parse XML files. * The factory is created according to javax.xml.parsers.SAXParserFactory property. * The parser has set entity resolver to system entity resolver chain. * @param validate if true validating parser is returned * @throws FactoryConfigurationError * @return sax parser or null if no parser can be created * @deprecated Use {@link XMLUtil#createXMLReader(boolean,boolean ) Util} instead * setting ns to false. * For more details see {@link #createParser() createParser} */ public static Parser createParser (boolean validate) { Parser parser = XMLDataObjectImpl.makeParser(validate); parser.setEntityResolver(getChainingEntityResolver()); return parser; } /** * Creates empty DOM Document using JAXP factoring. * @return Document or null on problems with JAXP factoring * @deprecated Replaced with {@link XMLUtil#createDocument(String,String,String,String) XMLUtil} * It directly violates DOM's root element reference read-only status. * If you can not move to XMLUtil for compatabilty reasons please * replace with following workaround: *

     * String templ = "";
     * InputSource in = new InputSource(new StringReader(templ));
     * in.setSystemId("StringReader");  //workaround
     * DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
     * Document doc = builder.parse(in);
     * 
*/ public static Document createDocument() { deprecated(); try { return XMLDataObjectImpl.makeBuilder(false).newDocument(); } catch (IOException ex) { return null; } catch (SAXException ex) { return null; } } /** * Writes DOM Document to writer. * * @param doc DOM Document to be written * @param writer OutoutStreamWriter preffered otherwise * encoding will be left for implementation specific autodection * * @deprecated Encoding used by Writer * may be in direct conflict with encoding * declared in document. Replaced with {@link XMLUtil#write(Document, OutputStream, String) Util}. */ public static void write (Document doc, Writer writer) throws IOException { deprecated(); // WARNING: back compatability code //use reflection to access "friendly" implementation in other package final String FAILURE = "org.openide.xml.XMLUtilImpl.write() invocation failed."; //NOI18N try { Class clzz = Class.forName("org.openide.xml.XMLUtilImpl"); //NOI18N Method impl = clzz.getDeclaredMethod("write", new Class[] {//NOI18N Document.class, Object.class, String.class }); impl.setAccessible(true); impl.invoke(null, new Object[] {doc, writer, null}); } catch (IllegalAccessException ex) { throw new IOException(FAILURE); } catch (IllegalArgumentException ex) { throw new IOException(FAILURE); } catch (NoSuchMethodException ex) { throw new IOException(FAILURE); } catch (ClassNotFoundException ex) { throw new IOException(FAILURE); } catch (InvocationTargetException ex) { Throwable t = ex.getTargetException(); if (t instanceof IOException) { throw (IOException) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } throw new IOException(FAILURE); } } /** * Write Document into OutputStream using given encoding. * It is a shortcut for writing configurations etc. It guarantee * just that data will be written. Structure and indentation * may change. * * @param doc DOM Document to be written * @param out data sink * @param enc - XML defined encoding name (i.e. IANA defined, one of UTF-8, UNICODE, ASCII). * @deprecated Moved to {@link XMLUtil#write(Document, OutputStream, String) XMLUtil}. */ public static void write(Document doc, OutputStream out, String enc) throws IOException { XMLUtil.write(doc, out, enc); } /** * Creates SAX InputSource for specified URL * @deprecated Deprecated as it was a workaround method. Replace * with new InputSource(url.toExternalForm()). */ public static org.xml.sax.InputSource createInputSource (URL url) throws IOException { return new InputSource(url.toExternalForm()); } /** * Registers the given public ID as corresponding to a particular * URI, typically a local copy. This URI will be used in preference * to ones provided as system IDs in XML entity declarations. This * mechanism would most typically be used for Document Type Definitions * (DTDs), where the public IDs are formally managed and versioned. * *

Any created parser use global entity resolver and you can * register its catalog entry. * * @param publicId The managed public ID being mapped * @param uri The URI of the preferred copy of that entity * * @deprecated Do not rely on global (non-modular) resolvers. * Use {@link EntityCatalog} and {@link XMLUtil} * instead. */ public static void registerCatalogEntry (String publicId, String uri) { if (publicId == null) throw new IllegalArgumentException("null public ID is not allowed."); //NOI18N XMLDataObjectImpl.registerCatalogEntry(publicId, uri); } /** * Registers a given public ID as corresponding to a particular Java * resource in a given class loader, typically distributed with a * software package. This resource will be preferred over system IDs * included in XML documents. This mechanism should most typically be * used for Document Type Definitions (DTDs), where the public IDs are * formally managed and versioned. * *

If a mapping to a URI has been provided, that mapping takes * precedence over this one. * *

Any created parser use global entity resolver and you can * register its catalog entry. * * @param publicId The managed public ID being mapped * @param resourceName The name of the Java resource * @param loader The class loader holding the resource, or null if * it is a system resource. * * @deprecated Do not rely on global (non-modular) resolvers. * Use {@link EntityCatalog} and {@link XMLUtil} * instead. */ public static void registerCatalogEntry (String publicId, String resourceName, ClassLoader loader) { if (publicId == null) throw new IllegalArgumentException("null public ID is not allowed."); //NOI18N XMLDataObjectImpl.registerCatalogEntry(publicId, "nbres:/" + resourceName); //NOI18N } /** * Add a given entity resolver to IDE resolver chain. * The resolver chain is searched by private chaining resolver * until some registered resolver succed. * *

Every created parser use global entity resolver and then chain. * * @deprecated EntityResolver is a parser user responsibility. * Every time set a EntityResolver to an XML parser you use. * The OpenIDE now defines a system {@link EntityCatalog}. * * @param resolver non null resolver to be added * * @return true if successfully added */ public static final boolean addEntityResolver(EntityResolver resolver) { // return false; Is is deprecated :-) return getChainingEntityResolver().addEntityResolver(resolver); } /** * Remove a given entity resolver from IDE resolver chain. * *

Every created parser use global entity resolver and then chain. * * @deprecated EntityResolver is a parser user responsibility. * * @param resolver non null resolver to be removed * @return removed resolver instance or null if not present */ public static final EntityResolver removeEntityResolver(EntityResolver resolver) { return getChainingEntityResolver().removeEntityResolver(resolver); } /** Accessor method for chaining entity resolver implementation. */ private static synchronized XMLEntityResolverChain getChainingEntityResolver() { if (chainingEntityResolver == null) { chainingEntityResolver = new XMLEntityResolverChain(); chainingEntityResolver.addEntityResolver(getSystemResolver()); } return chainingEntityResolver; } /** Lazy initialized system resolver. */ private static EntityResolver getSystemResolver() { return EntityCatalog.getDefault(); } /** * Registers new Info to particular XML document content type as * recognized by DTD public id. The registration is valid until IDE JVM termination. * * @param publicId used as key * @param info associated value or null to unregister * * @deprecated Register an {@link Environment} via lookup, see * {@link XMLDataObject some details}. */ public static void registerInfo (String publicId, Info info) { //!!! to be replaced by lookup synchronized (infos) { if (info == null) { infos.remove(publicId); } else { infos.put(publicId, info); } } } /** * Obtain registered Info for particular DTD public ID. * * @param publicId key which value is required * @return Info clone that is used for given publicId or null * * @deprecated Register via lookup */ public static Info getRegisteredInfo(String publicId) { //!!! to be replaced by lookup synchronized (infos) { Info ret = (Info) infos.get(publicId); return ret == null ? null : (Info)ret.clone (); } } // ~~~~~~~~~~~~~~~~~~~ PRIVATE AREA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // dangerous, enter on your own risk // WARNING - wormhole called via reflection from core layer, // do not delete, rename, etc. /** Used for creating RuntimeCatalog instance */ private static EntityCatalog createEntityCatalog() { return XMLDataObjectImpl.createEntityCatalog(); } /* * Guess from stack trace who calls deprecated method and print it. */ private static void deprecated() { StringWriter wr = new StringWriter(); PrintWriter pr = new PrintWriter(wr); new Exception("").printStackTrace(pr); // NOI18N pr.flush(); String stack = wr.toString().trim(); int start = stack.indexOf("\n"); // NOI18N int end = stack.indexOf("\n", start + 1); // NOI18N while (stack.indexOf("XMLDataObject", start + 1 )>0) { // NOI18N start = end; end = stack.indexOf("\n", start + 1); // NOI18N } String line = stack.substring(start + 1, end).trim(); System.out.println("Warning: deprecated method called " + line); // NOI18N } /** * Default ErrorHandler reporting to log. */ static class ErrorPrinter implements org.xml.sax.ErrorHandler { private void message (final String level, final org.xml.sax.SAXParseException e) { ErrorManager em = ErrorManager.getDefault().getInstance("org.openide.loaders.XMLDataObject"); // NOI18N if (!em.isLoggable(ErrorManager.INFORMATIONAL)) { return; } final String msg = NbBundle.getMessage( XMLDataObject.class, "PROP_XmlMessage", //NOI18N new Object [] { level, e.getMessage(), e.getSystemId() == null ? "" : e.getSystemId(), // NOI18N "" + e.getLineNumber(), // NOI18N "" + e.getColumnNumber() // NOI18N } ); em.log(msg); } public void error (org.xml.sax.SAXParseException e) { message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlError"), e); //NOI18N } public void warning (org.xml.sax.SAXParseException e) { message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlWarning"), e); //NOI18N } public void fatalError (org.xml.sax.SAXParseException e) { message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlFatalError"), e); //NOI18N } } // end of inner class ErrorPrinter //~~~~~~~~~~~~~~~~~~~~~~~~ PARSER ---------------------------------- // internally stops documet parsing when looking for public id private static class StopSaxException extends SAXException { public StopSaxException() { super("STOP"); } //NOI18N } // static fields that that are logically a part of InfoParser private static final StopSaxException STOP = new StopSaxException(); /** We are guaranteed to be executed in one thread let reuse parser, etc. */ private static XMLReader sharedParserImpl = null; static { try { sharedParserImpl = XMLUtil.createXMLReader(); sharedParserImpl.setEntityResolver(new EmptyEntityResolver()); } catch (SAXException ex) { ErrorManager err = ErrorManager.getDefault(); err.annotate(ex, "System does not contain JAXP 1.1 compliant parser!"); // NOI18N err.notify(err.ERROR, ex); } //initialize stuff possibly needed by libs that do not use //JAXP but SAX 2 directly try { final Properties props = System.getProperties(); final String SAX2_KEY = "org.xml.sax.driver"; //NOI18N if (props.getProperty(SAX2_KEY) == null) { props.put(SAX2_KEY, sharedParserImpl.getClass().getName()); } } catch (RuntimeException ex) { //ignore it (we did the best efford) } } /** A template to ask lookup for */ private static final Lookup.Template TEMPLATE = new Lookup.Template (Node.Cookie.class); /** a string to signal null value for parsedId */ private static final String NULL = ""; // NOI18N /** * It simulates null that forbiden by SAX specs. */ private static class NullHandler extends DefaultHandler implements LexicalHandler { static final NullHandler INSTANCE = new NullHandler(); NullHandler() {} // LexicalHandler public void startDTD(String root, String pID, String sID) throws SAXException { } public void endDTD() throws SAXException { } public void startEntity(String name) throws SAXException { } public void endEntity(String name) throws SAXException { } public void startCDATA() throws SAXException { } public void endCDATA() throws SAXException { } public void comment(char[] ch, int start, int length) throws SAXException { } } /** * Parser that parses XML document header to get some hints from it (such as DOCTYPE public ID. * It is named as InfoParser for historical reasons as it originally parsed * a coupled file (with xmlinfo extension) that contained IDE related metadata. * That mechanism was replaced by /xml/lookup/ registrations and * by file attributes. */ private final class InfoParser extends DefaultHandler implements FileChangeListener, LexicalHandler, LookupListener { /** the result of parsing the IDE */ private String parsedId; /** Lookup associated with this document. */ private Lookup lookup; /** result used for this lookup */ private Lookup.Result result; private ThreadLocal QUERY = new ThreadLocal (); InfoParser() {} //~~~~~~~~~~~~~~~~~~~~~ Task body and control of queue ~~~~~~~~~~~~~~~~~~~ /** Getter for public ID of the document. */ public String getPublicId () { waitFinished (); return parsedId == NULL ? null : parsedId; } /** Does lookup for specific cookie. It also * calls lookup on the result of Node.Cookie.class query and that is why it * initializes the listeners to notify changes made on the data object. * * @param class to look for */ public Object lookupCookie (final Class clazz) { if (QUERY.get () == clazz) { // somebody is querying for the same cookie in the same thread // probably neverending-loop - ignore return new InstanceCookie () { public Class instanceClass () { return clazz; } public Object instanceCreate () throws IOException { throw new IOException ("Cyclic reference, sorry: " + clazz); } public String instanceName () { return clazz.getName (); } }; } Object previous = QUERY.get (); try { QUERY.set (clazz); waitFinished (); Lookup l = lookup != null ? lookup : Lookup.EMPTY; Lookup.Result r = result; if (r != null) { // just to initialize all listeners r.allItems (); } return l.lookup (clazz); } finally { QUERY.set (previous); } } /* * Find out DTD public ID. * Info is then assigned according to it from registry. */ public void waitFinished () { waitFinished (null); } /* * Find out DTD public ID. * Info is then assigned according to it from registry. */ private void waitFinished (String ignorePreviousId) { if (sharedParserImpl == null) return; XMLReader parser = sharedParserImpl; FileObject myFileObject = getPrimaryFile(); String previousID; String newID = null; previousID = parsedId; if (parsedId != null) { // ok, has already been parsed return; } URL url = null; InputStream in = null; try { url = myFileObject.getURL(); } catch (IOException ex) { warning(ex, "I/O exception while retrieving xml FileObject URL."); //NOI18N return; // cannot parse } synchronized (this) { try { if (!myFileObject.isValid()) return; parsedId = NULL; try { in = myFileObject.getInputStream(); } catch (IOException ex) { warning(ex, "I/O exception while openning xml."); //NOI18N return; // cannot parse } try { // // we use one shared parser instance, so we must protect // its integrity // synchronized (sharedParserImpl) { configureParser(parser, false, this); parser.setContentHandler(this); parser.setErrorHandler(this); InputSource input = new InputSource(url.toExternalForm()); input.setByteStream(in); parser.parse (input); } } catch (StopSaxException stopped) { newID = parsedId; } catch (SAXException checkStop) { // stop parsing anyway if (STOP.getMessage ().equals (checkStop.getMessage ())) { newID = parsedId; } else { String msg = "Thread:" + Thread.currentThread().getName(); //NOI18N emgr().annotate(checkStop, "DocListener should not throw SAXException but STOP one.\n" + msg); //NOI18N emgr().notify(emgr().INFORMATIONAL, checkStop); Exception ex = checkStop.getException(); if (ex != null) { emgr().notify(emgr().INFORMATIONAL, ex); } } } catch (FileNotFoundException ex) { // thrown when there is a problem with URL for example emgr().notify(emgr().INFORMATIONAL, ex); } catch (IOException ex) { // error while parsing for public id hide it. // somebody have deleted the file meanwhile, because I do not lock? emgr().notify(emgr().INFORMATIONAL, ex); } finally { // such small memory leak can complicate profiling a lot // on the other hand it might cause performance regression // guard it by dedicated property if (Boolean.getBoolean("netbeans.profile.memory")) { // NOI18N // dettach from shared impl, it is static! parser.setContentHandler(NullHandler.INSTANCE); parser.setErrorHandler(NullHandler.INSTANCE); try { parser.setProperty("http://xml.org/sax/properties/lexical-handler", NullHandler.INSTANCE); //NOI18N } catch (SAXException ignoreIt) { } try { // Crimson requires it to release old properties and handlers parser.parse((InputSource)null); } catch (Exception ignoreIt) { } } parser = null; } } finally { try { if (in != null) in.close(); } catch (IOException ex) { emgr().notify(emgr().INFORMATIONAL, ex); } } } if (ignorePreviousId != null && newID.equals (ignorePreviousId)) { // no updates in lookup return; } // out of any synchronized blocks udpate the lookup // because it can call into unknown places via its // Environment.findForOne if (newID != null) { updateLookup (previousID, newID); } } /** Updates the ID. */ private void updateLookup (String previousID, String id) { if (previousID != null && previousID.equals (id)) { return; } Lookup newLookup; // no lock here, because createInfoLookup & findForOne can call // foreing code Info info = getRegisteredInfo (id); if (info != null) { // use info newLookup = createInfoLookup (XMLDataObject.this, info); } else { // ask the environment for the lookups newLookup = Environment.findForOne (XMLDataObject.this); if (newLookup == null) { newLookup = Lookup.EMPTY; } } synchronized (this) { // just one update of lookup in this InfoParser Lookup.Result prevRes = result; lookup = newLookup; result = lookup.lookup (TEMPLATE); result.addLookupListener (this); if (prevRes != null) { prevRes.removeLookupListener (this); XMLDataObject.this.firePropertyChange (DataObject.PROP_COOKIE, null, null); } } } /* * We reuse the parser so it must be reconfigured prior every parsing task. */ private void configureParser(XMLReader parser, boolean validation, LexicalHandler lex) { try { parser.setFeature("http://xml.org/sax/features/validation", validation); //NOI18N } catch (SAXException sex) { emgr().log("Warning: XML parser does not support validation feature."); //NOI18N } try { parser.setProperty("http://xml.org/sax/properties/lexical-handler", lex); //NOI18N } catch (SAXException sex) { emgr().log("Warning: XML parser does not support lexical-handler feature."); //NOI18N //throw new Error(""); } } // ~~~~~~~~~~ ERROR REPORTING ~~~~~~~~~~~~~~~~~~~~~~ public void warning (Throwable ex) { warning(ex, null); } public void warning (Throwable ex, String annotation) { ErrorManager emgr = emgr(); if (annotation != null) emgr.annotate(ex, annotation); emgr.notify(ErrorManager.INFORMATIONAL, ex); //do not show until in debug mode } // LexicalHandler public void startDTD(String root, String pID, String sID) throws SAXException { parsedId = pID == null ? NULL : pID; stop(); } public void endDTD() throws SAXException { stop(); } public void startEntity(String name) throws SAXException { } public void endEntity(String name) throws SAXException { } public void startCDATA() throws SAXException { } public void endCDATA() throws SAXException { } public void comment(char[] ch, int start, int length) throws SAXException { } // redefine DefaultHandler //!!! should we stop on error? public void error(final org.xml.sax.SAXParseException p1) throws org.xml.sax.SAXException { stop(); } public void fatalError(final org.xml.sax.SAXParseException p1) throws org.xml.sax.SAXException { stop(); } public void endDocument() throws org.xml.sax.SAXException { stop(); } public void startElement(String uri, String lName, String qName, Attributes atts) throws org.xml.sax.SAXException { // no DTD present stop(); } private void stop() throws SAXException { throw STOP; } //~~~~~~~~~~~~~~~~~~ FS LISTENER ~~~~~~~~~~~~~~~~~ //listening at parent folder public void fileFolderCreated (FileEvent fe) { // not interesting } public void fileDataCreated (FileEvent fe) { // FileObject fo = fe.getFile(); // fileCreated(fo); } private void fileCreated(FileObject fo) { // if ( // fo.getName ().equals (getPrimaryFile ().getName ()) // ) { // // new info file created => force it to be reparsed // resolveInfo(); // } } /** Fired when a file is changed. * @param fe the event describing context where action has taken place */ public void fileChanged (FileEvent fe) { if (getPrimaryFile ().equals (fe.getFile ())) { // the main file changed => invalidate DOM document //resolveInfo (getPrimaryFile ()); //reparse info again clearDocument (); String prevId = parsedId; parsedId = null; // parse update only if the ID is different waitFinished (prevId); } } public void fileDeleted (FileEvent fe) { } public void fileRenamed (FileRenameEvent fe) { // // the same behaviour as when the file is deleted // fileDeleted (fe); // // and new created // fileCreated(fe.getFile()); } public void fileAttributeChanged (FileAttributeEvent fe) { // not interested in } /** A change in lookup. */ public void resultChanged(org.openide.util.LookupEvent lookupEvent) { XMLDataObject.this.firePropertyChange (DataObject.PROP_COOKIE, null, null); Node n = XMLDataObject.this.getNodeDelegateOrNull (); if (n instanceof XMLNode) { ((XMLNode)n).update (); } } } // end of InfoParser /** Avoid Internet connections */ private static class EmptyEntityResolver implements EntityResolver { EmptyEntityResolver() {} public InputSource resolveEntity(String publicId, String systemID) { InputSource ret = new InputSource(new StringReader("")); //??? we should tolerate file: and nbfs: // NOI18N ret.setSystemId("StringReader"); //NOI18N return ret; } } // ~~~~~~~~~~~~~~~~~~~~~~~~~~ private Loader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** The DataLoader for XmlDataObjects. */ static final class Loader extends MultiFileLoader { static final long serialVersionUID =3917883920409453930L; /** Creates a new XMLDataLoader */ public Loader () { super ("org.openide.loaders.XMLDataObject"); //!!! so the relation loader data object is fixed // NOI18N //super (XMLDataObject.class); // nothing like looks loader can be constructed } // can it produce subclasses? /** Get default actions. * @return array of default system actions or null if this loader does not have any * actions */ protected SystemAction[] defaultActions () { return new SystemAction[] { SystemAction.get(OpenAction.class), SystemAction.get(FileSystemAction.class), null, SystemAction.get(CutAction.class), SystemAction.get(CopyAction.class), SystemAction.get(PasteAction.class), null, SystemAction.get(DeleteAction.class), SystemAction.get(RenameAction.class), null, SystemAction.get(SaveAsTemplateAction.class), null, SystemAction.get(ToolsAction.class), SystemAction.get(PropertiesAction.class) }; } /** Get the default display name of this loader. * @return default display name */ protected String defaultDisplayName () { return NbBundle.getMessage (XMLDataObject.class, "PROP_XmlLoader_Name"); } /** For a given file finds a primary file. * @param fo the file to find primary file for * * @return the primary file for the file or null if the file is not * recognized by this loader */ protected FileObject findPrimaryFile (FileObject fo) { String mime = fo.getMIMEType (); if (MIME.equals(mime) || "application/xml".equals(mime)) { //NOI18N return fo; } /* JST: I believe that this can be removed because XML_EXT should * always be recognized as text/xml mime type * * Cc.: Unforunatelly there are .xml configuration files * that use "*+xml" suffixed MIME content type * for lightweight data-object-subclass-less typing purposes. * E.g. ProjectEnvironmentProviders are keyed by MIME type. * * It could be better to accept directly "+xml"s... */ if ("xml".equals(fo.getExt())) { return fo; } /** JST: Removed JSP should handle this in better way if ("tld".equals(fo.getExt())) { // NOI18N return fo; // JSP Tag Library Descriptor } */ // not recognized return null; } /** Creates the right data object for given primary file. * It is guaranteed that the provided file is realy primary file * returned from the method findPrimaryFile. * * @param primaryFile the primary file * @return the data object for this file * @exception DataObjectExistsException if the primary file already has data object */ protected MultiDataObject createMultiObject (FileObject primaryFile) throws DataObjectExistsException { return new XMLDataObject (primaryFile, this); } /** Creates the right primary entry for given primary file. * * @param primaryFile primary file recognized by this loader * @return primary entry for that file */ protected MultiDataObject.Entry createPrimaryEntry (MultiDataObject obj, FileObject primaryFile) { return new FileEntry (obj, primaryFile); } /** Creates right secondary entry for given file. The file is said to * belong to an object created by this loader. * * @param secondaryFile secondary file for which we want to create entry * @return the entry */ protected MultiDataObject.Entry createSecondaryEntry (MultiDataObject obj, FileObject secondaryFile) { // JST: We do not have secondary entries anymore, but it probably does not matter... return new FileEntry (obj, secondaryFile); } } // ~~~~~~~~~~~~~~~~~~~~~~ extension support via info ~~~~~~~~~~~~~~~~~~~~~~~~ // i would like to throw it away sometimes in future and replace it by better one /** This class has to be implemented by all processors in the * xmlinfo file. It is cookie, so after parsing such class is instantiated * and put into data objects cookie set. * * @deprecated use lookup */ public static interface Processor extends Node.Cookie { /** When the XMLDataObject creates new instance of the processor, * it uses this method to attach the processor to the data object. * * @param xmlDO XMLDataObject */ public void attachTo (XMLDataObject xmlDO); } /** @deprecated use Lookup * Representation of xmlinfo file holding container of Processors. */ public static final class Info implements Cloneable { List processors; String iconBase; /** Create info */ public Info () { processors = new ArrayList (); iconBase = null; } public Object clone () { Info ii = new Info(); for (Iterator it = processors.iterator(); it.hasNext();) { Class proc = (Class)it.next(); ii.processors.add (proc); } ii.iconBase = iconBase; return ii; } /** Add processor class to info. * The class should be public and either implement the Processor * interface or should * have public constructor with one argument (DataObject or XMLDataObject). * * @param proc the class to add to this info * @exception IllegalArgumentException if the class does not seem to be valid */ public synchronized void addProcessorClass (Class proc) { if (!Processor.class.isAssignableFrom (proc)) { Constructor[] arr = proc.getConstructors(); for (int i = 0; i < arr.length; i++) { Class[] params = arr[i].getParameterTypes(); if (params.length == 1) { if ( params[0] == DataObject.class || params[0] == XMLDataObject.class ) { arr = null; break; } } } if (arr != null) { // no suitable constructor throw new IllegalArgumentException(); } } processors.add (proc); } /** Remove processor class from info. * @return true if removed */ public boolean removeProcessorClass (Class proc) { return processors.remove (proc); } public Iterator processorClasses () { return processors.iterator(); } /** Set icon base */ public void setIconBase (String base) { iconBase = base; } /** @return icon base */ public String getIconBase () { return iconBase; } /** Write specified info to writer */ public void write (Writer writer) throws IOException { throw new IOException ("Not supported anymore"); // NOI18N /* writer.write ("\n\n"); // NOI18N writer.write (MessageFormat.format ("\n\n", // NOI18N new Object [] { InfoParser.TAG_INFO, XMLINFO_DTD_PUBLIC_ID })); writer.write (MessageFormat.format ("<{0}>\n", // NOI18N new Object [] { InfoParser.TAG_INFO })); for (Iterator it = processors.iterator(); it.hasNext();) writer.write (MessageFormat.format (" <{0} {1}=\"{2}\" />\n", // NOI18N new Object [] { InfoParser.TAG_PROCESSOR, InfoParser.ATT_PROCESSOR_CLASS, ((Class)it.next()).getName() })); if (iconBase != null) writer.write (MessageFormat.format (" <{0} {1}=\"{2}\" />\n", // NOI18N new Object [] { InfoParser.TAG_ICON, InfoParser.ATT_ICON_BASE, iconBase })); writer.write (MessageFormat.format ("\n", // NOI18N new Object [] { InfoParser.TAG_INFO })); */ } public boolean equals (Object obj) { if (obj == null) return false; if (obj instanceof Info == false) return false; Info i = (Info) obj; return ((iconBase != null && iconBase.equals(i.iconBase)) || (i.iconBase == iconBase)) && processors.equals(i.processors); } } // end of inner class Info /** A method for backward compatibility to create a lookup from data object and info * @param obj xml data object * @param info the info that should be associated */ static Lookup createInfoLookup (XMLDataObject obj, Info info) { return new InfoLkp (obj, info); } /** A backward compatibility class that converts the content of * an Info object into a Lookup class. */ private static final class InfoLkp extends AbstractLookup { public final Info info; public InfoLkp (XMLDataObject obj, Info info) { this.info = info; Iterator it = info.processorClasses (); ArrayList arr = new ArrayList (info.processors.size ()); while (it.hasNext ()) { Class c = (Class)it.next (); arr.add (new InfoPair (obj, c)); } setPairs (arr); } /** A pair that receives a class and can create its instance either * using default constructor or by passing data object into one * argument constructor. */ private static final class InfoPair extends AbstractLookup.Pair { /** the class to use or null if object has already been created */ private Class clazz; /** XMLDataObject associated or object created */ private Object obj; /** For use by subclasses. */ protected InfoPair (XMLDataObject obj, Class c) { this.obj = obj; this.clazz = c; } /** Tests whether this item can produce object * of class c. */ protected boolean instanceOf (Class c) { Class temp = clazz; if (temp == null) { return c.isInstance (obj); } else { return c.isAssignableFrom (temp); } } /** Method that can test whether an instance of a class has been created * by this item. * * @param obj the instance * @return if the item has already create an instance and it is the same * as obj. */ protected boolean creatorOf (Object obj) { return this.obj == obj; } /** The class of the result item. * @return the instance of the object. */ public synchronized Object getInstance () { if (clazz == null) { // already created an object return obj; } // after this method the obj or null will contain the created object // instead of reference to XMLDataObject XMLDataObject xmlDataObject = (XMLDataObject)obj; obj = null; // the clazz will be null to signal, that an instance // of object has been created Class next = clazz; clazz = null; try { if (Processor.class.isAssignableFrom (next)) { // the class implements Processor interface, so use // default constructor to construct instance obj = next.newInstance (); Processor proc = (Processor) obj; proc.attachTo (xmlDataObject); return obj; } else { // does not implement processor, try to search // for constructor with one argument of DataObject or // XMLDataObject Constructor[] arr = next.getConstructors(); for (int i = 0; i < arr.length; i++) { Class[] params = arr[i].getParameterTypes(); if (params.length == 1) { if ( params[0] == DataObject.class || params[0] == XMLDataObject.class ) { obj = arr[i].newInstance( new Object[] { xmlDataObject } ); return obj; } } } } throw new InternalError ("XMLDataObject processor class " + next + " invalid"); // NOI18N } catch (InvocationTargetException e) { xmlDataObject.notifyEx (e); } catch (InstantiationException e) { xmlDataObject.notifyEx(e); } catch (IllegalAccessException e) { xmlDataObject.notifyEx(e); } return obj; } /** The class of the result item. * @return the class of the item */ public Class getType () { Class temp = clazz; return temp != null ? temp : obj.getClass (); } /** A persistent indentifier of the item. Can be stored and use * in next run of the system. * * @return a string id of the item */ public String getId () { return "Info[" + getType ().getName (); // NOI18N } /** The best display name is probably the name of type... */ public String getDisplayName () { return getType ().getName (); } } } /** Computes correct node for given XMLDataObject. */ private Node findNode () { Node n = (Node)getIP ().lookupCookie (Node.class); if (n == null) { DataNode d = new DataNode (XMLDataObject.this, Children.LEAF); d.setIconBase ("org/openide/loaders/xmlObject"); // NOI18N d.setDefaultAction (SystemAction.get (OpenAction.class)); return d; } else { return n; } } /** Node that delegates either to data node or to a node provided by * the data object itself. */ private final class XMLNode extends FilterNode { public XMLNode (XMLDataObject obj) { this (obj.findNode ()); } private XMLNode (Node del) { super (del, new FilterNode.Children (del)); //setShortDescription("XML FILE"); } private void update () { changeOriginal (XMLDataObject.this.findNode (), true); } } /** A special delegator that adds InstanceCookie.Of to objects that miss it */ private static class ICDel extends Object implements InstanceCookie.Of { /** object we belong to */ private XMLDataObject obj; /** cookie we delegate to */ private InstanceCookie ic; public ICDel (XMLDataObject obj, InstanceCookie ic) { this.obj = obj; this.ic = ic; } public String instanceName () { return ic.instanceName (); } public Class instanceClass () throws java.io.IOException, ClassNotFoundException { return ic.instanceClass (); } public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { return ic.instanceCreate (); } public boolean instanceOf (Class cls2) { if (ic instanceof InstanceCookie.Of) { return ((InstanceCookie.Of) ic).instanceOf (cls2); } else { try { return cls2.isAssignableFrom (instanceClass ()); } catch (IOException ioe) { // ignore exception return false; } catch (ClassNotFoundException cnfe) { // ignore exception return false; } } } public int hashCode () { return 2 * obj.hashCode () + ic.hashCode (); } public boolean equals (Object obj) { if (obj instanceof ICDel) { ICDel d = (ICDel)obj; return d.obj == obj && d.ic == ic; } return false; } } // end of ICDel /** Delegating DOM document that provides fast implementation of * getDocumentType and getPublicID methods. */ private final class DelDoc implements InvocationHandler { private Reference/**/ xmlDocument; private final Document proxyDocument; DelDoc() { proxyDocument = (Document)Proxy.newProxyInstance( DelDoc.class.getClassLoader(), new Class[] {Document.class}, this); } /** Creates w3c's document for the xml file. Either returns cached reference * or parses the file and creates new document. * * @param force really create the document if it does not exists yet? * @return the parsed document or null if not forced */ private final Document getDocumentImpl (boolean force) { synchronized (this) { Object doc = xmlDocument == null ? null : xmlDocument.get (); if (doc instanceof Document) { return (Document)doc; } if (!force) { return null; } status = STATUS_OK; try { Document d = parsePrimaryFile (); xmlDocument = new SoftReference (d); return d; } catch (SAXException e) { emgr ().notify (ErrorManager.INFORMATIONAL, e); } catch (IOException e) { emgr ().notify (ErrorManager.INFORMATIONAL, e); } status = STATUS_ERROR; Document d = XMLUtil.createDocument("brokenDocument", null, null, null); // NOI18N xmlDocument = new SoftReference (d); // fire property change, because the document is errornous firePropertyChange (PROP_DOCUMENT, null, null); return d; } } /** * Get the externally usable, lazy document. * Delegates everything to the parsed document on disk (parsing as necessary), * except that getDoctype().getPublicId() is specially implemented so as to * not require loading the whole document. */ public Document getProxyDocument() { return proxyDocument; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("getDoctype") && args == null) { // NOI18N return Proxy.newProxyInstance(DelDoc.class.getClassLoader(), new Class[] {DocumentType.class}, this); } else if (method.getName().equals("getPublicId") && args == null) { // NOI18N Document d = getDocumentImpl(false); if (d != null) { DocumentType doctype = d.getDoctype(); return doctype == null ? null : doctype.getPublicId(); } else { return getIP().getPublicId(); } } else { return method.invoke(getDocumentImpl(true), args); } } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.