View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Licensed to the Apache Software Foundation (ASF) under one or more
7    * contributor license agreements.  See the NOTICE file distributed with
8    * this work for additional information regarding copyright ownership.
9    * The ASF licenses this file to You under the Apache License, Version 2.0
10   * (the "License"); you may not use this file except in compliance with
11   * the License.  You may obtain a copy of the License at
12   *
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   */
21  
22  package com.sun.org.apache.xerces.internal.impl.xs.traversers;
23  
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  
27  import javax.xml.stream.XMLEventReader;
28  import javax.xml.stream.XMLStreamConstants;
29  import javax.xml.stream.XMLStreamException;
30  import javax.xml.stream.XMLStreamReader;
31  import javax.xml.stream.events.Attribute;
32  import javax.xml.stream.events.EndElement;
33  import javax.xml.stream.events.Namespace;
34  import javax.xml.stream.events.ProcessingInstruction;
35  import javax.xml.stream.events.StartElement;
36  import javax.xml.stream.events.XMLEvent;
37  
38  import com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOMParser;
39  import com.sun.org.apache.xerces.internal.util.JAXPNamespaceContextWrapper;
40  import com.sun.org.apache.xerces.internal.util.StAXLocationWrapper;
41  import com.sun.org.apache.xerces.internal.util.SymbolTable;
42  import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
43  import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
44  import com.sun.org.apache.xerces.internal.util.XMLSymbols;
45  import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
46  import com.sun.org.apache.xerces.internal.xni.QName;
47  import com.sun.org.apache.xerces.internal.xni.XMLString;
48  import com.sun.org.apache.xerces.internal.xni.XNIException;
49  import org.w3c.dom.Document;
50  
51  /**
52   * <p>StAXSchemaParser reads StAX events, converts them into XNI events
53   * and passes them directly to the SchemaDOMParser.</p>
54   *
55   * @xerces.internal
56   *
57   * @version $Id: StAXSchemaParser.java,v 1.2 2010-10-26 23:01:12 joehw Exp $
58   */
59  final class StAXSchemaParser {
60  
61      /** Chunk size (1024). */
62      private static final int CHUNK_SIZE = (1 << 10);
63  
64      /** Chunk mask (CHUNK_SIZE - 1). */
65      private static final int CHUNK_MASK = CHUNK_SIZE - 1;
66  
67      /** Array for holding character data. **/
68      private final char [] fCharBuffer = new char[CHUNK_SIZE];
69  
70      /** Symbol table **/
71      private SymbolTable fSymbolTable;
72  
73      /** SchemaDOMParser, events will be delegated to SchemaDOMParser to pass */
74      private SchemaDOMParser fSchemaDOMParser;
75  
76      /** XML Locator wrapper for SAX. **/
77      private final StAXLocationWrapper fLocationWrapper = new StAXLocationWrapper();
78  
79      /** The namespace context of this document: stores namespaces in scope */
80      private final JAXPNamespaceContextWrapper fNamespaceContext = new JAXPNamespaceContextWrapper(fSymbolTable);
81  
82      /** Fields for start element, end element and characters. */
83      private final QName fElementQName = new QName();
84      private final QName fAttributeQName = new QName();
85      private final XMLAttributesImpl fAttributes = new XMLAttributesImpl();
86      private final XMLString fTempString = new XMLString();
87      private final ArrayList fDeclaredPrefixes = new ArrayList();
88      private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
89      private int fDepth;
90  
91      public StAXSchemaParser() {
92          fNamespaceContext.setDeclaredPrefixes(fDeclaredPrefixes);
93      }
94  
95      public void reset(SchemaDOMParser schemaDOMParser, SymbolTable symbolTable) {
96          fSchemaDOMParser = schemaDOMParser;
97          fSymbolTable = symbolTable;
98          fNamespaceContext.setSymbolTable(fSymbolTable);
99          fNamespaceContext.reset();
100     }
101 
102     public Document getDocument() {
103         return fSchemaDOMParser.getDocument();
104     }
105 
106     public void parse(XMLEventReader input) throws XMLStreamException, XNIException {
107         XMLEvent currentEvent = input.peek();
108         if (currentEvent != null) {
109             int eventType = currentEvent.getEventType();
110             if (eventType != XMLStreamConstants.START_DOCUMENT &&
111                 eventType != XMLStreamConstants.START_ELEMENT) {
112                 throw new XMLStreamException();
113             }
114             fLocationWrapper.setLocation(currentEvent.getLocation());
115             fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
116             loop: while (input.hasNext()) {
117                 currentEvent = input.nextEvent();
118                 eventType = currentEvent.getEventType();
119                 switch (eventType) {
120                 case XMLStreamConstants.START_ELEMENT:
121                     ++fDepth;
122                     StartElement start = currentEvent.asStartElement();
123                     fillQName(fElementQName, start.getName());
124                     fLocationWrapper.setLocation(start.getLocation());
125                     fNamespaceContext.setNamespaceContext(start.getNamespaceContext());
126                     fillXMLAttributes(start);
127                     fillDeclaredPrefixes(start);
128                     addNamespaceDeclarations();
129                     fNamespaceContext.pushContext();
130                     fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
131                     break;
132                 case XMLStreamConstants.END_ELEMENT:
133                     EndElement end = currentEvent.asEndElement();
134                     fillQName(fElementQName, end.getName());
135                     fillDeclaredPrefixes(end);
136                     fLocationWrapper.setLocation(end.getLocation());
137                     fSchemaDOMParser.endElement(fElementQName, null);
138                     fNamespaceContext.popContext();
139                     --fDepth;
140                     if (fDepth <= 0) {
141                         break loop;
142                     }
143                     break;
144                 case XMLStreamConstants.CHARACTERS:
145                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
146                     break;
147                 case XMLStreamConstants.SPACE:
148                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), true);
149                     break;
150                 case XMLStreamConstants.CDATA:
151                     fSchemaDOMParser.startCDATA(null);
152                     sendCharactersToSchemaParser(currentEvent.asCharacters().getData(), false);
153                     fSchemaDOMParser.endCDATA(null);
154                     break;
155                 case XMLStreamConstants.PROCESSING_INSTRUCTION:
156                     ProcessingInstruction pi = (ProcessingInstruction)currentEvent;
157                     fillProcessingInstruction(pi.getData());
158                     fSchemaDOMParser.processingInstruction(pi.getTarget(), fTempString, null);
159                     break;
160                 case XMLStreamConstants.DTD:
161                     /* There shouldn't be a DTD in the schema */
162                     break;
163                 case XMLStreamConstants.ENTITY_REFERENCE:
164                     /* Not needed for schemas */
165                     break;
166                 case XMLStreamConstants.COMMENT:
167                     /* No point in sending comments */
168                     break;
169                 case XMLStreamConstants.START_DOCUMENT:
170                     fDepth++;
171                     /* We automatically call startDocument before the loop */
172                     break;
173                 case XMLStreamConstants.END_DOCUMENT:
174                     /* We automatically call endDocument after the loop */
175                     break;
176                 }
177             }
178             fLocationWrapper.setLocation(null);
179             fNamespaceContext.setNamespaceContext(null);
180             fSchemaDOMParser.endDocument(null);
181         }
182     }
183 
184     public void parse(XMLStreamReader input) throws XMLStreamException, XNIException {
185         if (input.hasNext()) {
186             int eventType = input.getEventType();
187             if (eventType != XMLStreamConstants.START_DOCUMENT &&
188                 eventType != XMLStreamConstants.START_ELEMENT) {
189                 throw new XMLStreamException();
190             }
191             fLocationWrapper.setLocation(input.getLocation());
192             fSchemaDOMParser.startDocument(fLocationWrapper, null, fNamespaceContext, null);
193             boolean first = true;
194             loop: while (input.hasNext()) {
195                 if (!first) {
196                     eventType = input.next();
197                 }
198                 else {
199                     first = false;
200                 }
201                 switch (eventType) {
202                 case XMLStreamConstants.START_ELEMENT:
203                     ++fDepth;
204                     fLocationWrapper.setLocation(input.getLocation());
205                     fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
206                     fillQName(fElementQName, input.getNamespaceURI(),
207                         input.getLocalName(), input.getPrefix());
208                     fillXMLAttributes(input);
209                     fillDeclaredPrefixes(input);
210                     addNamespaceDeclarations();
211                     fNamespaceContext.pushContext();
212                     fSchemaDOMParser.startElement(fElementQName, fAttributes, null);
213                     break;
214                 case XMLStreamConstants.END_ELEMENT:
215                     fLocationWrapper.setLocation(input.getLocation());
216                     fNamespaceContext.setNamespaceContext(input.getNamespaceContext());
217                     fillQName(fElementQName, input.getNamespaceURI(),
218                         input.getLocalName(), input.getPrefix());
219                     fillDeclaredPrefixes(input);
220                     fSchemaDOMParser.endElement(fElementQName, null);
221                     fNamespaceContext.popContext();
222                     --fDepth;
223                     if (fDepth <= 0) {
224                         break loop;
225                     }
226                     break;
227                 case XMLStreamConstants.CHARACTERS:
228                     fTempString.setValues(input.getTextCharacters(),
229                         input.getTextStart(), input.getTextLength());
230                     fSchemaDOMParser.characters(fTempString, null);
231                     break;
232                 case XMLStreamConstants.SPACE:
233                     fTempString.setValues(input.getTextCharacters(),
234                         input.getTextStart(), input.getTextLength());
235                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
236                     break;
237                 case XMLStreamConstants.CDATA:
238                     fSchemaDOMParser.startCDATA(null);
239                     fTempString.setValues(input.getTextCharacters(),
240                         input.getTextStart(), input.getTextLength());
241                     fSchemaDOMParser.characters(fTempString, null);
242                     fSchemaDOMParser.endCDATA(null);
243                     break;
244                 case XMLStreamConstants.PROCESSING_INSTRUCTION:
245                     fillProcessingInstruction(input.getPIData());
246                     fSchemaDOMParser.processingInstruction(input.getPITarget(), fTempString, null);
247                     break;
248                 case XMLStreamConstants.DTD:
249                     /* There shouldn't be a DTD in the schema */
250                     break;
251                 case XMLStreamConstants.ENTITY_REFERENCE:
252                     /* Not needed for schemas */
253                     break;
254                 case XMLStreamConstants.COMMENT:
255                     /* No point in sending comments */
256                     break;
257                 case XMLStreamConstants.START_DOCUMENT:
258                     ++fDepth;
259                     /* We automatically call startDocument before the loop */
260                     break;
261                 case XMLStreamConstants.END_DOCUMENT:
262                     /* We automatically call endDocument after the loop */
263                     break;
264                 }
265             }
266             fLocationWrapper.setLocation(null);
267             fNamespaceContext.setNamespaceContext(null);
268             fSchemaDOMParser.endDocument(null);
269         }
270     }
271 
272     /** Send characters to the validator in CHUNK_SIZE character chunks. */
273     private void sendCharactersToSchemaParser(String str, boolean whitespace) {
274         if (str != null) {
275             final int length = str.length();
276             final int remainder = length & CHUNK_MASK;
277             if (remainder > 0) {
278                 str.getChars(0, remainder, fCharBuffer, 0);
279                 fTempString.setValues(fCharBuffer, 0, remainder);
280                 if (whitespace) {
281                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
282                 }
283                 else {
284                     fSchemaDOMParser.characters(fTempString, null);
285                 }
286             }
287             int i = remainder;
288             while (i < length) {
289                 str.getChars(i, i += CHUNK_SIZE, fCharBuffer, 0);
290                 fTempString.setValues(fCharBuffer, 0, CHUNK_SIZE);
291                 if (whitespace) {
292                     fSchemaDOMParser.ignorableWhitespace(fTempString, null);
293                 }
294                 else {
295                     fSchemaDOMParser.characters(fTempString, null);
296                 }
297             }
298         }
299     }
300 
301     // processing instructions must be sent all in one chunk
302     private void fillProcessingInstruction(String data) {
303         final int dataLength = data.length();
304         char [] charBuffer = fCharBuffer;
305         if (charBuffer.length < dataLength) {
306             // toCharArray() creates a newly allocated array, so it's okay
307             // to keep a reference to it.
308             charBuffer = data.toCharArray();
309         }
310         else {
311             data.getChars(0, dataLength, charBuffer, 0);
312         }
313         fTempString.setValues(charBuffer, 0, dataLength);
314     }
315 
316     private void fillXMLAttributes(StartElement event) {
317         fAttributes.removeAllAttributes();
318         final Iterator attrs = event.getAttributes();
319         while (attrs.hasNext()) {
320             Attribute attr = (Attribute) attrs.next();
321             fillQName(fAttributeQName, attr.getName());
322             String type = attr.getDTDType();
323             int idx = fAttributes.getLength();
324             fAttributes.addAttributeNS(fAttributeQName,
325                     (type != null) ? type : XMLSymbols.fCDATASymbol, attr.getValue());
326             fAttributes.setSpecified(idx, attr.isSpecified());
327         }
328     }
329 
330     private void fillXMLAttributes(XMLStreamReader input) {
331         fAttributes.removeAllAttributes();
332         final int len = input.getAttributeCount();
333         for (int i = 0; i < len; ++i) {
334             fillQName(fAttributeQName, input.getAttributeNamespace(i),
335                 input.getAttributeLocalName(i), input.getAttributePrefix(i));
336             String type = input.getAttributeType(i);
337             fAttributes.addAttributeNS(fAttributeQName,
338                     (type != null) ? type : XMLSymbols.fCDATASymbol, input.getAttributeValue(i));
339             fAttributes.setSpecified(i, input.isAttributeSpecified(i));
340         }
341     }
342 
343     private void addNamespaceDeclarations() {
344         String prefix = null;
345         String localpart = null;
346         String rawname = null;
347         String nsPrefix = null;
348         String nsURI = null;
349 
350         final Iterator iter = fDeclaredPrefixes.iterator();
351         while (iter.hasNext()) {
352             nsPrefix = (String) iter.next();
353             nsURI = fNamespaceContext.getURI(nsPrefix);
354             if (nsPrefix.length() > 0) {
355                 prefix = XMLSymbols.PREFIX_XMLNS;
356                 localpart = nsPrefix;
357                 fStringBuffer.clear();
358                 fStringBuffer.append(prefix);
359                 fStringBuffer.append(':');
360                 fStringBuffer.append(localpart);
361                 rawname = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
362             }
363             else {
364                 prefix = XMLSymbols.EMPTY_STRING;
365                 localpart = XMLSymbols.PREFIX_XMLNS;
366                 rawname = XMLSymbols.PREFIX_XMLNS;
367             }
368             fAttributeQName.setValues(prefix, localpart, rawname, NamespaceContext.XMLNS_URI);
369             fAttributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol,
370                     (nsURI != null) ? nsURI : XMLSymbols.EMPTY_STRING);
371         }
372     }
373 
374     /** Fills in the list of declared prefixes. */
375     private void fillDeclaredPrefixes(StartElement event) {
376         fillDeclaredPrefixes(event.getNamespaces());
377     }
378 
379     /** Fills in the list of declared prefixes. */
380     private void fillDeclaredPrefixes(EndElement event) {
381         fillDeclaredPrefixes(event.getNamespaces());
382     }
383 
384     /** Fills in the list of declared prefixes. */
385     private void fillDeclaredPrefixes(Iterator namespaces) {
386         fDeclaredPrefixes.clear();
387         while (namespaces.hasNext()) {
388             Namespace ns = (Namespace) namespaces.next();
389             String prefix = ns.getPrefix();
390             fDeclaredPrefixes.add(prefix != null ? prefix : "");
391         }
392     }
393 
394     /** Fills in the list of declared prefixes. */
395     private void fillDeclaredPrefixes(XMLStreamReader reader) {
396         fDeclaredPrefixes.clear();
397         final int len = reader.getNamespaceCount();
398         for (int i = 0; i < len; ++i) {
399             String prefix = reader.getNamespacePrefix(i);
400             fDeclaredPrefixes.add(prefix != null ? prefix : "");
401         }
402     }
403 
404     /** Fills in a QName object. */
405     private void fillQName(QName toFill, javax.xml.namespace.QName toCopy) {
406         fillQName(toFill, toCopy.getNamespaceURI(), toCopy.getLocalPart(), toCopy.getPrefix());
407     }
408 
409     /** Fills in a QName object. */
410     final void fillQName(QName toFill, String uri, String localpart, String prefix) {
411         uri = (uri != null && uri.length() > 0) ? fSymbolTable.addSymbol(uri) : null;
412         localpart = (localpart != null) ? fSymbolTable.addSymbol(localpart) : XMLSymbols.EMPTY_STRING;
413         prefix = (prefix != null && prefix.length() > 0) ? fSymbolTable.addSymbol(prefix) : XMLSymbols.EMPTY_STRING;
414         String raw = localpart;
415         if (prefix != XMLSymbols.EMPTY_STRING) {
416             fStringBuffer.clear();
417             fStringBuffer.append(prefix);
418             fStringBuffer.append(':');
419             fStringBuffer.append(localpart);
420             raw = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
421         }
422         toFill.setValues(prefix, localpart, raw, uri);
423     }
424 
425 } // StAXSchemaParser