View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 1999-2005 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *      http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package com.sun.org.apache.xerces.internal.dom;
21  
22  import com.sun.org.apache.xerces.internal.impl.RevalidationHandler;
23  import com.sun.org.apache.xerces.internal.parsers.DOMParserImpl;
24  import com.sun.org.apache.xerces.internal.parsers.DTDConfiguration;
25  import com.sun.org.apache.xerces.internal.parsers.XIncludeAwareParserConfiguration;
26  import com.sun.org.apache.xerces.internal.util.XMLChar;
27  import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
28  import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
29  import com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl;
30  import org.w3c.dom.DOMException;
31  import org.w3c.dom.DOMImplementation;
32  import org.w3c.dom.Document;
33  import org.w3c.dom.DocumentType;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.ls.LSParser;
36  import org.w3c.dom.ls.DOMImplementationLS;
37  import org.w3c.dom.ls.LSInput;
38  import org.w3c.dom.ls.LSOutput;
39  import org.w3c.dom.ls.LSSerializer;
40  /**
41   * The DOMImplementation class is description of a particular
42   * implementation of the Document Object Model. As such its data is
43   * static, shared by all instances of this implementation.
44   * <P>
45   * The DOM API requires that it be a real object rather than static
46   * methods. However, there's nothing that says it can't be a singleton,
47   * so that's how I've implemented it.
48   * <P>
49   * This particular class, along with CoreDocumentImpl, supports the DOM
50   * Core and Load/Save (Experimental). Optional modules are supported by
51   * the more complete DOMImplementation class along with DocumentImpl.
52   *
53   * @xerces.internal
54   *
55   * @version $Id: CoreDOMImplementationImpl.java,v 1.6 2010-11-01 04:39:37 joehw Exp $
56   * @since PR-DOM-Level-1-19980818.
57   */
58  public class CoreDOMImplementationImpl
59          implements DOMImplementation, DOMImplementationLS {
60          //
61          // Data
62          //
63  
64      // validators pool
65      private static final int SIZE = 2;
66      private RevalidationHandler validators[] = new RevalidationHandler[SIZE];
67  
68      private RevalidationHandler dtdValidators[] = new RevalidationHandler[SIZE];
69      private int freeValidatorIndex = -1;
70      private int freeDTDValidatorIndex = -1;
71      private int currentSize = SIZE;
72  
73      // Document and doctype counter.  Used to assign order to documents and
74      // doctypes without owners, on an demand basis.   Used for
75      // compareDocumentPosition
76      private int docAndDoctypeCounter = 0;
77  
78          // static
79          /** Dom implementation singleton. */
80          static CoreDOMImplementationImpl singleton =
81                  new CoreDOMImplementationImpl();
82          //
83          // Public methods
84          //
85          /** NON-DOM: Obtain and return the single shared object */
86          public static DOMImplementation getDOMImplementation() {
87                  return singleton;
88          }
89          //
90          // DOMImplementation methods
91          //
92          /**
93           * Test if the DOM implementation supports a specific "feature" --
94           * currently meaning language and level thereof.
95           *
96           * @param feature The package name of the feature to test.
97           * In Level 1, supported values are "HTML" and "XML" (case-insensitive).
98           * At this writing, com.sun.org.apache.xerces.internal.dom supports only XML.
99           *
100          * @param version The version number of the feature being tested.
101          * This is interpreted as "Version of the DOM API supported for the
102          * specified Feature", and in Level 1 should be "1.0"
103          *
104          * @return true iff this implementation is compatable with the specified
105          * feature and version.
106          */
107         public boolean hasFeature(String feature, String version) {
108 
109             boolean anyVersion = version == null || version.length() == 0;
110 
111             // check if Xalan implementation is around and if yes report true for supporting
112             // XPath API
113             // if a plus sign "+" is prepended to any feature name, implementations
114             // are considered in which the specified feature may not be directly
115             // castable DOMImplementation.getFeature(feature, version). Without a
116             // plus, only features whose interfaces are directly castable are considered.
117             if ((feature.equalsIgnoreCase("+XPath"))
118                 && (anyVersion || version.equals("3.0"))) {
119                 try {
120                     Class xpathClass = ObjectFactory.findProviderClass(
121                         "com.sun.org.apache.xpath.internal.domapi.XPathEvaluatorImpl", true);
122 
123                 // Check if the DOM XPath implementation implements
124                 // the interface org.w3c.dom.XPathEvaluator
125                 Class interfaces[] = xpathClass.getInterfaces();
126                 for (int i = 0; i < interfaces.length; i++) {
127                     if (interfaces[i].getName().equals(
128                         "org.w3c.dom.xpath.XPathEvaluator")) {
129                         return true;
130                     }
131                 }
132                 } catch (Exception e) {
133                     return false;
134                 }
135                 return true;
136             }
137             if (feature.startsWith("+")) {
138                 feature = feature.substring(1);
139             }
140             return (
141                 feature.equalsIgnoreCase("Core")
142                     && (anyVersion
143                         || version.equals("1.0")
144                         || version.equals("2.0")
145                         || version.equals("3.0")))
146                         || (feature.equalsIgnoreCase("XML")
147                     && (anyVersion
148                         || version.equals("1.0")
149                         || version.equals("2.0")
150                         || version.equals("3.0")))
151                         || (feature.equalsIgnoreCase("LS")
152                     && (anyVersion || version.equals("3.0")));
153         } // hasFeature(String,String):boolean
154 
155 
156         /**
157          * Introduced in DOM Level 2. <p>
158          *
159          * Creates an empty DocumentType node.
160          *
161          * @param qualifiedName The qualified name of the document type to be created.
162          * @param publicID The document type public identifier.
163          * @param systemID The document type system identifier.
164          * @since WD-DOM-Level-2-19990923
165          */
166         public DocumentType createDocumentType( String qualifiedName,
167                                     String publicID, String systemID) {
168                 // REVISIT: this might allow creation of invalid name for DOCTYPE
169                 //          xmlns prefix.
170                 //          also there is no way for a user to turn off error checking.
171                 checkQName(qualifiedName);
172                 return new DocumentTypeImpl(null, qualifiedName, publicID, systemID);
173         }
174 
175     final void checkQName(String qname){
176         int index = qname.indexOf(':');
177         int lastIndex = qname.lastIndexOf(':');
178         int length = qname.length();
179 
180         // it is an error for NCName to have more than one ':'
181         // check if it is valid QName [Namespace in XML production 6]
182         if (index == 0 || index == length - 1 || lastIndex != index) {
183             String msg =
184                 DOMMessageFormatter.formatMessage(
185                     DOMMessageFormatter.DOM_DOMAIN,
186                     "NAMESPACE_ERR",
187                     null);
188             throw new DOMException(DOMException.NAMESPACE_ERR, msg);
189         }
190         int start = 0;
191         // Namespace in XML production [6]
192         if (index > 0) {
193             // check that prefix is NCName
194             if (!XMLChar.isNCNameStart(qname.charAt(start))) {
195                 String msg =
196                     DOMMessageFormatter.formatMessage(
197                         DOMMessageFormatter.DOM_DOMAIN,
198                         "INVALID_CHARACTER_ERR",
199                         null);
200                 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
201             }
202             for (int i = 1; i < index; i++) {
203                 if (!XMLChar.isNCName(qname.charAt(i))) {
204                     String msg =
205                         DOMMessageFormatter.formatMessage(
206                             DOMMessageFormatter.DOM_DOMAIN,
207                             "INVALID_CHARACTER_ERR",
208                             null);
209                     throw new DOMException(
210                         DOMException.INVALID_CHARACTER_ERR,
211                         msg);
212                 }
213             }
214             start = index + 1;
215         }
216 
217         // check local part
218         if (!XMLChar.isNCNameStart(qname.charAt(start))) {
219             // REVISIT: add qname parameter to the message
220             String msg =
221                 DOMMessageFormatter.formatMessage(
222                     DOMMessageFormatter.DOM_DOMAIN,
223                     "INVALID_CHARACTER_ERR",
224                     null);
225             throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
226         }
227         for (int i = start + 1; i < length; i++) {
228             if (!XMLChar.isNCName(qname.charAt(i))) {
229                 String msg =
230                     DOMMessageFormatter.formatMessage(
231                         DOMMessageFormatter.DOM_DOMAIN,
232                         "INVALID_CHARACTER_ERR",
233                         null);
234                 throw new DOMException(DOMException.INVALID_CHARACTER_ERR, msg);
235             }
236         }
237     }
238 
239 
240         /**
241          * Introduced in DOM Level 2. <p>
242          *
243          * Creates an XML Document object of the specified type with its document
244          * element.
245          *
246          * @param namespaceURI     The namespace URI of the document
247          *                         element to create, or null.
248          * @param qualifiedName    The qualified name of the document
249          *                         element to create.
250          * @param doctype          The type of document to be created or null.<p>
251          *
252          *                         When doctype is not null, its
253          *                         Node.ownerDocument attribute is set to
254          *                         the document being created.
255          * @return Document        A new Document object.
256          * @throws DOMException    WRONG_DOCUMENT_ERR: Raised if doctype has
257          *                         already been used with a different document.
258          * @since WD-DOM-Level-2-19990923
259          */
260         public Document createDocument(
261                 String namespaceURI,
262                 String qualifiedName,
263                 DocumentType doctype)
264                 throws DOMException {
265                 if (doctype != null && doctype.getOwnerDocument() != null) {
266                         String msg =
267                                 DOMMessageFormatter.formatMessage(
268                                         DOMMessageFormatter.DOM_DOMAIN,
269                                         "WRONG_DOCUMENT_ERR",
270                                         null);
271                         throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
272                 }
273                 CoreDocumentImpl doc = new CoreDocumentImpl(doctype);
274                 Element e = doc.createElementNS(namespaceURI, qualifiedName);
275                 doc.appendChild(e);
276                 return doc;
277         }
278 
279         /**
280          * DOM Level 3 WD - Experimental.
281          */
282         public Object getFeature(String feature, String version) {
283             if (singleton.hasFeature(feature, version)) {
284                 if ((feature.equalsIgnoreCase("+XPath"))) {
285                     try {
286                         Class xpathClass = ObjectFactory.findProviderClass(
287                             "com.sun.org.apache.xpath.internal.domapi.XPathEvaluatorImpl", true);
288                         // Check if the DOM XPath implementation implements
289                         // the interface org.w3c.dom.XPathEvaluator
290                         Class interfaces[] = xpathClass.getInterfaces();
291                         for (int i = 0; i < interfaces.length; i++) {
292                             if (interfaces[i].getName().equals(
293                                 "org.w3c.dom.xpath.XPathEvaluator")) {
294                                 return xpathClass.newInstance();
295                             }
296                         }
297                     } catch (Exception e) {
298                         return null;
299                     }
300                 } else {
301                     return singleton;
302                 }
303             }
304             return null;
305         }
306 
307         // DOM L3 LS
308 
309         /**
310          * DOM Level 3 LS CR - Experimental.
311      * Create a new <code>LSParser</code>. The newly constructed parser may
312      * then be configured by means of its <code>DOMConfiguration</code>
313      * object, and used to parse documents by means of its <code>parse</code>
314      *  method.
315      * @param mode  The <code>mode</code> argument is either
316      *   <code>MODE_SYNCHRONOUS</code> or <code>MODE_ASYNCHRONOUS</code>, if
317      *   <code>mode</code> is <code>MODE_SYNCHRONOUS</code> then the
318      *   <code>LSParser</code> that is created will operate in synchronous
319      *   mode, if it's <code>MODE_ASYNCHRONOUS</code> then the
320      *   <code>LSParser</code> that is created will operate in asynchronous
321      *   mode.
322      * @param schemaType  An absolute URI representing the type of the schema
323      *   language used during the load of a <code>Document</code> using the
324      *   newly created <code>LSParser</code>. Note that no lexical checking
325      *   is done on the absolute URI. In order to create a
326      *   <code>LSParser</code> for any kind of schema types (i.e. the
327      *   LSParser will be free to use any schema found), use the value
328      *   <code>null</code>.
329      * <p ><b>Note:</b>    For W3C XML Schema [<a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/'>XML Schema Part 1</a>]
330      *   , applications must use the value
331      *   <code>"http://www.w3.org/2001/XMLSchema"</code>. For XML DTD [<a href='http://www.w3.org/TR/2000/REC-xml-20001006'>XML 1.0</a>],
332      *   applications must use the value
333      *   <code>"http://www.w3.org/TR/REC-xml"</code>. Other Schema languages
334      *   are outside the scope of the W3C and therefore should recommend an
335      *   absolute URI in order to use this method.
336      * @return  The newly created <code>LSParser</code> object. This
337      *   <code>LSParser</code> is either synchronous or asynchronous
338      *   depending on the value of the <code>mode</code> argument.
339      * <p ><b>Note:</b>    By default, the newly created <code>LSParser</code>
340      *    does not contain a <code>DOMErrorHandler</code>, i.e. the value of
341      *   the "<a href='http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030609/core.html#parameter-error-handler'>
342      *   error-handler</a>" configuration parameter is <code>null</code>. However, implementations
343      *   may provide a default error handler at creation time. In that case,
344      *   the initial value of the <code>"error-handler"</code> configuration
345      *   parameter on the new created <code>LSParser</code> contains a
346      *   reference to the default error handler.
347      * @exception DOMException
348      *    NOT_SUPPORTED_ERR: Raised if the requested mode or schema type is
349      *   not supported.
350          */
351         public LSParser createLSParser(short mode, String schemaType)
352                 throws DOMException {
353                 if (mode != DOMImplementationLS.MODE_SYNCHRONOUS || (schemaType !=null &&
354                    !"http://www.w3.org/2001/XMLSchema".equals(schemaType) &&
355                         !"http://www.w3.org/TR/REC-xml".equals(schemaType))) {
356                         String msg =
357                                 DOMMessageFormatter.formatMessage(
358                                         DOMMessageFormatter.DOM_DOMAIN,
359                                         "NOT_SUPPORTED_ERR",
360                                         null);
361                         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg);
362                 }
363                 if (schemaType != null
364                         && schemaType.equals("http://www.w3.org/TR/REC-xml")) {
365                         return new DOMParserImpl(new DTDConfiguration(),
366                                 schemaType);
367                 }
368                 else {
369                         // create default parser configuration validating against XMLSchemas
370                         return new DOMParserImpl(new XIncludeAwareParserConfiguration(),
371                                 schemaType);
372                 }
373         }
374 
375         /**
376          * DOM Level 3 LS CR - Experimental.
377          * Create a new <code>LSSerializer</code> object.
378          * @return The newly created <code>LSSerializer</code> object.
379          * <p ><b>Note:</b>    By default, the newly created
380          * <code>LSSerializer</code> has no <code>DOMErrorHandler</code>,
381          * i.e. the value of the <code>"error-handler"</code> configuration
382          * parameter is <code>null</code>. However, implementations may
383          * provide a default error handler at creation time. In that case, the
384          * initial value of the <code>"error-handler"</code> configuration
385          * parameter on the new created <code>LSSerializer</code> contains a
386          * reference to the default error handler.
387          */
388         public LSSerializer createLSSerializer() {
389         return new DOMSerializerImpl();
390     }
391         /**
392          * DOM Level 3 LS CR - Experimental.
393          * Create a new empty input source.
394          * @return  The newly created input object.
395          */
396         public LSInput createLSInput() {
397                 return new DOMInputImpl();
398         }
399 
400         //
401         // Protected methods
402         //
403         /** NON-DOM: retrieve validator. */
404         synchronized RevalidationHandler getValidator(String schemaType) {
405                 // REVISIT: implement retrieving DTD validator
406         if (schemaType == XMLGrammarDescription.XML_SCHEMA) {
407             // create new validator - we should not attempt
408             // to restrict the number of validation handlers being
409             // requested
410             if(freeValidatorIndex < 0) {
411                 return (RevalidationHandler) (ObjectFactory
412                             .newInstance(
413                                 "com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator",
414                                 ObjectFactory.findClassLoader(),
415                                 true));
416             }
417             // return first available validator
418             RevalidationHandler val = validators[freeValidatorIndex];
419             validators[freeValidatorIndex--] = null;
420             return val;
421         }
422         else if(schemaType == XMLGrammarDescription.XML_DTD) {
423             if(freeDTDValidatorIndex < 0) {
424                 return (RevalidationHandler) (ObjectFactory
425                             .newInstance(
426                                 "com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator",
427                                 ObjectFactory.findClassLoader(),
428                                 true));
429             }
430             // return first available validator
431             RevalidationHandler val = dtdValidators[freeDTDValidatorIndex];
432             dtdValidators[freeDTDValidatorIndex--] = null;
433             return val;
434         }
435         return null;
436         }
437 
438         /** NON-DOM: release validator */
439         synchronized void releaseValidator(String schemaType,
440                                          RevalidationHandler validator) {
441        // REVISIT: implement support for DTD validators as well
442        if(schemaType == XMLGrammarDescription.XML_SCHEMA) {
443            ++freeValidatorIndex;
444            if (validators.length == freeValidatorIndex ){
445                 // resize size of the validators
446                 currentSize+=SIZE;
447                 RevalidationHandler newarray[] =  new RevalidationHandler[currentSize];
448                 System.arraycopy(validators, 0, newarray, 0, validators.length);
449                 validators = newarray;
450            }
451            validators[freeValidatorIndex]=validator;
452        }
453        else if(schemaType == XMLGrammarDescription.XML_DTD) {
454            ++freeDTDValidatorIndex;
455            if (dtdValidators.length == freeDTDValidatorIndex ){
456                 // resize size of the validators
457                 currentSize+=SIZE;
458                 RevalidationHandler newarray[] =  new RevalidationHandler[currentSize];
459                 System.arraycopy(dtdValidators, 0, newarray, 0, dtdValidators.length);
460                 dtdValidators = newarray;
461            }
462            dtdValidators[freeDTDValidatorIndex]=validator;
463        }
464         }
465 
466        /** NON-DOM:  increment document/doctype counter */
467        protected synchronized int assignDocumentNumber() {
468             return ++docAndDoctypeCounter;
469        }
470        /** NON-DOM:  increment document/doctype counter */
471        protected synchronized int assignDocTypeNumber() {
472             return ++docAndDoctypeCounter;
473        }
474 
475     /* DOM Level 3 LS CR - Experimental.
476      *
477      * Create a new empty output destination object where
478      * <code>LSOutput.characterStream</code>,
479      * <code>LSOutput.byteStream</code>, <code>LSOutput.systemId</code>,
480      * <code>LSOutput.encoding</code> are null.
481 
482      * @return  The newly created output object.
483      */
484        public LSOutput createLSOutput() {
485            return new DOMOutputImpl();
486        }
487 
488 } // class DOMImplementationImpl