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