| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.openide.loaders; |
| |
| |
| import java.io.*; |
| import java.lang.ref.*; |
| import java.lang.reflect.*; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.logging.*; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.FactoryConfigurationError; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.netbeans.modules.openide.loaders.RuntimeCatalog; |
| import org.openide.cookies.*; |
| import org.openide.filesystems.*; |
| import org.openide.nodes.*; |
| import org.openide.text.DataEditorSupport; |
| import org.openide.util.*; |
| import org.openide.util.lookup.AbstractLookup; |
| import org.openide.windows.CloneableOpenSupport; |
| import org.openide.xml.*; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.xml.sax.*; |
| import org.xml.sax.ext.LexicalHandler; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * Object that provides main functionality for xml documents. |
| * These objects are recognized by the <code>xml</code> extension and |
| * <code>text/xml</code> MIME type. |
| * <p> |
| * It is declaratively extensible by an {@link Environment}. |
| * The <code>Environment</code> is assigned to document instances using a provider |
| * registered by DOCTYPE's public ID in the system filesystem under |
| * <code>xml/lookups/{Transformed-DOCTYPE}</code> 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 |
| */ |
| @Deprecated |
| public static final String XMLINFO_DTD_PUBLIC_ID_FORTE = "-//Forte for Java//DTD xmlinfo//EN"; // NOI18N |
| /** @deprecated replaced with Lookup |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| private static XMLEntityResolverChain chainingEntityResolver; |
| |
| /** map of DTD publicID => Info. */ |
| private static HashMap<String, Info> infos = new HashMap<String, Info>(); |
| // 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. |
| */ |
| @Deprecated |
| 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 XMLDataObjectInfoParser infoParser; |
| |
| /* For logging and debugging. */ |
| static final Logger ERR = Logger.getLogger(XMLDataObject.class.getName()); |
| |
| |
| /** |
| * Create new XMLDataObject. It is usually called by a loader. |
| * A user can get existing XMLDataObject by calling {@link DataObject#find(FileObject) |
| * <code>DataObject.find(FileObject f)</code>} instead. |
| * |
| * @param fo the primary file object, never <code>null</code> |
| * @param loader loader of this data object, never <code>null</code> |
| */ |
| public XMLDataObject (FileObject fo, MultiFileLoader loader) |
| throws DataObjectExistsException { |
| this(fo, loader, true); |
| } |
| |
| /** |
| * Constructs XMLDataObject without any registered cookies (for editor, |
| * open, etc.). Useful for subclasses. |
| * |
| * @param fo the primary file object, never <code>null</code> |
| * @param loader loader of this data object, never <code>null</code> |
| * @param registerEditor call with false to skip registrations of various |
| * editor related cookies |
| * @since 7.10 |
| */ |
| protected XMLDataObject (FileObject fo, MultiFileLoader loader, boolean registerEditor) |
| throws DataObjectExistsException { |
| super (fo, loader); |
| |
| status = STATUS_NOT; |
| |
| if (registerEditor) { |
| registerEditor(); |
| } |
| } |
| |
| private void registerEditor() { |
| // 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 <T extends Node.Cookie> T createCookie(Class<T> 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()) ? klass.cast(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 |
| cookies.assign( SaveAsCapable.class, new SaveAsCapable() { |
| public void saveAs( FileObject folder, String fileName ) throws IOException { |
| EditorCookie ec = getCookieSet().getCookie( EditorCookie.class ); |
| if( ec instanceof DataEditorSupport ) { |
| ((DataEditorSupport)ec).saveAs( folder, fileName ); |
| } else { |
| Logger.getLogger( XMLDataObject.class.getName() ).log( Level.FINE, "'Save As' requires DataEditorSupport" ); //NOI18N |
| } |
| } |
| }); |
| } |
| |
| /** Getter for info parser. Initializes the infoparser in "lazy" way so it is accessble even before |
| * the constructor finishes. |
| */ |
| private final XMLDataObjectInfoParser getIP () { |
| synchronized (emgrLock) { |
| if (infoParser == null) { |
| infoParser = new XMLDataObjectInfoParser (this); |
| } |
| } |
| 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 |
| */ |
| @Override |
| protected Node createNodeDelegate () { |
| return new XMLNode(this); |
| } |
| |
| |
| |
| /** 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 |
| */ |
| @Deprecated |
| 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. |
| * <p> |
| * 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). |
| */ |
| @Override |
| public <T extends Node.Cookie> T getCookie(Class<T> cls) { |
| getIP ().waitFinished(); |
| |
| Object cake = getIP().lookupCookie(cls); |
| |
| if (ERR.isLoggable(Level.FINE)) { |
| ERR.fine("Query for " + cls + " for " + this); // NOI18N |
| ERR.fine("Gives a cake " + cake + " for " + this); // NOI18N |
| } |
| |
| if (cake instanceof InstanceCookie) { |
| cake = ofCookie ((InstanceCookie)cake, cls); |
| } |
| |
| if (ERR.isLoggable(Level.FINE)) { |
| ERR.fine("After ofCookie: " + cake + " for " + this); // NOI18N |
| } |
| |
| if (cake == null) { |
| cake = super.getCookie (cls); |
| } |
| |
| if (ERR.isLoggable(Level.FINE)) { |
| ERR.fine("getCookie returns " + cake + " for " + this); // NOI18N |
| } |
| if (cake instanceof Node.Cookie) { |
| assert cake == null || cls.isInstance(cake) : "Cannot return " + cake + " for " + cls + " from " + this; |
| return cls.cast(cake); |
| } |
| return null; |
| } |
| |
| @Override |
| public Lookup getLookup() { |
| if (getClass() == XMLDataObject.class) { |
| Node n = getNodeDelegateOrNull(); |
| if (n == null) { |
| setNodeDelegate(n = createNodeDelegate()); |
| } |
| return n.getLookup(); |
| } |
| return super.getLookup(); |
| } |
| |
| /** 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) { |
| Exceptions.attachLocalizedMessage(e, |
| "Cannot resolve following class in xmlinfo."); // NOI18N |
| Exceptions.printStackTrace(e); |
| } |
| |
| /** Allows subclasses to provide their own editor cookie. |
| * @return an editor cookie to be used as a result of <code>getCookie(EditorCookie.class)</code> |
| * |
| * @deprecated CookieSet factory should be used by subclasses instead. |
| */ |
| @Deprecated |
| 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)); |
| //when undelying fileobject has a mimetype defined, |
| //don't enforce text/xml on the editor document. |
| //be conservative and apply the new behaviour only when the mimetype is xml like.. |
| if (obj.getPrimaryFile().getMIMEType().indexOf("xml") == -1) { // NOI18N |
| 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 = getDataObject().getCookie(SaveCookie.class); |
| if (save != null) { |
| ((XMLDataObject) getDataObject ()).removeSaveCookie (save); |
| getDataObject ().setModified (false); |
| } |
| } |
| |
| @Override |
| protected Pane createPane() { |
| if (MultiDOEditor.isMultiViewAvailable()) { |
| MultiDataObject mdo = (MultiDataObject) getDataObject(); |
| return MultiDOEditor.createMultiViewPane("text/plain", mdo); // NOI18N |
| } |
| return super.createPane(); |
| } |
| |
| //!!! 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 { |
| if (ERR.isLoggable(Level.FINE)) ERR.fine("getDocument" + " for " + this); |
| synchronized (this) { |
| DelDoc d = doc; |
| if (d == null) { |
| d = new DelDoc(this); |
| doc = d; |
| } |
| return d.getProxyDocument(); |
| } |
| } |
| |
| /** Clears the document. Called when the document file is changed. |
| */ |
| final void clearDocument () { |
| if (ERR.isLoggable(Level.FINE)) ERR.fine("clearDocument" + " for " + this); |
| //err.notify (ErrorManager.INFORMATIONAL, new Throwable ("stack dump")); |
| doc = null; |
| firePropertyChange (PROP_DOCUMENT, null, null); |
| } |
| |
| @Override |
| void notifyFileChanged(FileEvent fe) { |
| super.notifyFileChanged(fe); |
| getIP().fileChanged(fe); |
| } |
| |
| /** |
| * @return one of STATUS_XXX constants representing PROP_DOCUMENT state. |
| */ |
| public final int getStatus () { |
| return status; |
| } |
| |
| /** @deprecated not used anymore |
| * @return null |
| */ |
| @Deprecated |
| public final Info getInfo () { |
| return null; |
| } |
| |
| /** @deprecated does not do anything useful |
| */ |
| @Deprecated |
| public final synchronized void setInfo (Info ii) throws IOException { |
| } |
| |
| /** 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 { |
| if (ERR.isLoggable(Level.FINE)) ERR.fine("parsePrimaryFile" + " for " + this); |
| 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(); |
| } |
| } |
| } |
| |
| |
| // ~~~~~~~~~~~~~~~~~~~~ Start of Utilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| /** 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| public static Document parse (URL url, ErrorHandler eh, boolean validate) throws IOException, SAXException { |
| |
| return XMLUtil.parse (new InputSource(url.toExternalForm()),validate, false, eh, getChainingEntityResolver()); |
| } |
| |
| /** 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. |
| * <p>Alternatively if not interested in new callbacks defined by |
| * SAX 2.0 you can wrap returned XMLReader into XMLReaderAdapter |
| * that implements Parser. |
| */ |
| @Deprecated |
| 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)} instead |
| * setting ns to false. |
| * For more details see {@link #createParser() createParser} |
| */ |
| @Deprecated |
| public static Parser createParser (boolean validate) { |
| try { |
| Parser parser = new org.xml.sax.helpers.XMLReaderAdapter (XMLUtil.createXMLReader(validate)); |
| parser.setEntityResolver(getChainingEntityResolver()); |
| return parser; |
| } catch (SAXException ex) { |
| Exceptions.attachLocalizedMessage(ex, |
| "Can not create a SAX parser!\nCheck javax.xml.parsers.SAXParserFactory property features and the parser library presence on classpath."); // NOI18N |
| Exceptions.printStackTrace(ex); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * 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: |
| * <pre> |
| * String templ = "<myroot/>"; |
| * InputSource in = new InputSource(new StringReader(templ)); |
| * in.setSystemId("StringReader"); //workaround |
| * DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| * Document doc = builder.parse(in); |
| * </pre> |
| */ |
| @Deprecated |
| public static Document createDocument() { |
| |
| try { |
| DocumentBuilder builder; |
| DocumentBuilderFactory factory; |
| |
| //create factory according to javax.xml.parsers.SAXParserFactory property |
| //or platform default (i.e. com.sun...) |
| try { |
| factory = DocumentBuilderFactory.newInstance(); |
| factory.setValidating(false); |
| factory.setNamespaceAware(false); |
| } catch (FactoryConfigurationError err) { |
| Exceptions.attachLocalizedMessage(err, |
| "Can not create a factory!\nCheck " + |
| "javax.xml.parsers.DocumentBuilderFactory" + |
| " property and the factory library presence on classpath."); // NOI18N |
| Exceptions.printStackTrace(err); |
| return null; |
| } |
| |
| try { |
| builder = factory.newDocumentBuilder(); |
| } catch (ParserConfigurationException ex) { |
| SAXException sex = new SAXException("Configuration exception."); // NOI18N |
| sex.initCause(ex); |
| Exceptions.attachLocalizedMessage(sex, |
| "Can not create a DOM builder!\nCheck javax.xml.parsers.DocumentBuilderFactory property and the builder library presence on classpath."); // NOI18N |
| throw sex; |
| } |
| |
| return builder.newDocument(); |
| } 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}. |
| */ |
| @Deprecated |
| public static void write (Document doc, Writer writer) throws IOException { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| XMLUtil.write(doc, baos, "UTF-8"); |
| writer.write(baos.toString("UTF-8")); |
| } |
| |
| /** |
| * 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}. |
| */ |
| @Deprecated |
| 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 <code>new InputSource(url.toExternalForm())</code>. |
| */ |
| @Deprecated |
| public static 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. |
| * |
| * <P> 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. |
| */ |
| @Deprecated |
| public static void registerCatalogEntry (String publicId, String uri) { |
| Lookup.getDefault().lookup(RuntimeCatalog.class).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. |
| * |
| * <P> If a mapping to a URI has been provided, that mapping takes |
| * precedence over this one. |
| * |
| * <P> 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. |
| */ |
| @Deprecated |
| public static void registerCatalogEntry (String publicId, String resourceName, ClassLoader loader) { |
| Lookup.getDefault().lookup(RuntimeCatalog.class).registerCatalogEntry(publicId, resourceName, loader); |
| } |
| |
| /** |
| * Add a given entity resolver to the NetBeans resolver chain. |
| * The resolver chain is searched by private chaining resolver |
| * until some registered resolver succed. |
| * |
| * <P>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 |
| */ |
| @Deprecated |
| public static boolean addEntityResolver(EntityResolver resolver) { |
| // return false; Is is deprecated :-) |
| return getChainingEntityResolver().addEntityResolver(resolver); |
| } |
| |
| /** |
| * Remove a given entity resolver from the NetBeans resolver chain. |
| * |
| * <P>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 |
| */ |
| @Deprecated |
| public static EntityResolver removeEntityResolver(EntityResolver resolver) { |
| return getChainingEntityResolver().removeEntityResolver(resolver); |
| } |
| |
| |
| /** Accessor method for chaining entity resolver implementation. */ |
| @Deprecated |
| 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 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}. |
| */ |
| @Deprecated |
| 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 |
| */ |
| @Deprecated |
| public static Info getRegisteredInfo(String publicId) { //!!! to be replaced by lookup |
| synchronized (infos) { |
| Info ret = infos.get(publicId); |
| return ret == null ? null : (Info)ret.clone (); |
| } |
| } |
| |
| /** |
| * Default ErrorHandler reporting to log. |
| */ |
| static class ErrorPrinter implements ErrorHandler { |
| |
| private void message(final String level, final SAXParseException e) { |
| |
| if (!LOG.isLoggable(Level.FINE)) { |
| 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 |
| } |
| ); |
| |
| LOG.fine(msg); |
| } |
| |
| public void error(SAXParseException e) { |
| message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlError"), e); //NOI18N |
| } |
| |
| public void warning(SAXParseException e) { |
| message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlWarning"), e); //NOI18N |
| } |
| |
| public void fatalError(SAXParseException e) { |
| message (NbBundle.getMessage(XMLDataObject.class, "PROP_XmlFatalError"), e); //NOI18N |
| } |
| } // end of inner class ErrorPrinter |
| |
| |
| /** |
| * It simulates null that forbiden by SAX specs. |
| */ |
| 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 { |
| } |
| } |
| |
| |
| |
| |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~ 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? |
| |
| protected String actionsContext () { |
| return "Loaders/text/xml/Actions"; // NOI18N |
| } |
| |
| /** 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.endsWith("/xml") || mime.endsWith("+xml")) { // NOI18N |
| return fo; |
| } |
| // 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 {@link org.openide.loaders.Environment.Provider} instead. |
| */ |
| @Deprecated |
| 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. |
| */ |
| @Deprecated |
| public static final class Info implements Cloneable { |
| List<Class<?>> processors; |
| String iconBase; |
| |
| /** Create info */ |
| public Info () { |
| processors = new ArrayList<Class<?>> (); |
| iconBase = null; |
| } |
| |
| @Override |
| public Object clone () { |
| Info ii = new Info(); |
| for (Class<?> proc: processors) { |
| 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<Class<?>> 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 |
| } |
| |
| 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<Class<?>> it = info.processorClasses (); |
| ArrayList<InfoPair> arr = new ArrayList<InfoPair> (info.processors.size ()); |
| while (it.hasNext ()) { |
| Class<?> c = 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<?> cc = c; |
| Class<?> temp = clazz; |
| if (temp == null) { |
| return cc.isInstance (obj); |
| } else { |
| return cc.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) { |
| return new PlainDataNode(); |
| } else { |
| return n; |
| } |
| } |
| |
| private final class PlainDataNode extends DataNode { |
| public PlainDataNode() { |
| super(XMLDataObject.this, Children.LEAF); |
| setIconBaseWithExtension("org/openide/loaders/xmlObject.gif"); // NOI18N |
| } |
| } |
| |
| |
| /** Node that delegates either to data node or to a node provided by |
| * the data object itself. |
| */ |
| 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"); |
| } |
| final 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 |
| |
| static final Constructor<?> cnstr; |
| static { |
| try { |
| Class<?> proxy = Proxy.getProxyClass(XMLDataObject.class.getClassLoader(), Document.class, DocumentType.class); |
| cnstr = proxy.getConstructor(InvocationHandler.class); |
| new DelDoc(null); |
| } catch (NoSuchMethodException ex) { |
| throw new IllegalStateException(ex); |
| } |
| } /** Delegating DOM document that provides fast implementation of |
| * getDocumentType and getPublicID methods. |
| */ |
| private static final class DelDoc implements InvocationHandler { |
| private final XMLDataObject obj; |
| private Reference<Document> xmlDocument; |
| private final Document proxyDocument; |
| |
| DelDoc(XMLDataObject obj) { |
| this.obj = obj; |
| try { |
| proxyDocument = (Document) cnstr.newInstance(this); |
| } catch (Exception ex) { |
| throw new IllegalStateException(ex); |
| } |
| } |
| |
| /** 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) { |
| Document doc = xmlDocument == null ? null : xmlDocument.get (); |
| if (doc != null) { |
| return doc; |
| } |
| |
| if (!force) { |
| return null; |
| } |
| |
| obj.status = STATUS_OK; |
| try { |
| Document d = obj.parsePrimaryFile(); |
| xmlDocument = new SoftReference<Document> (d); |
| return d; |
| } catch (SAXException e) { |
| ERR.log(Level.WARNING, null, e); |
| } catch (IOException e) { |
| ERR.log(Level.WARNING, null, e); |
| } |
| |
| obj.status = STATUS_ERROR; |
| Document d = XMLUtil.createDocument("brokenDocument", null, null, null); // NOI18N |
| |
| xmlDocument = new SoftReference<Document> (d); |
| |
| // fire property change, because the document is errornous |
| obj.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 (DocumentType)proxyDocument; |
| } 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 obj.getIP().getPublicId(); |
| } |
| } else { |
| return method.invoke(getDocumentImpl(true), args); |
| } |
| } |
| |
| } |
| |
| } |