1 /*
2  * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.xml.internal.stream.buffer.stax;
27 
28 import com.sun.xml.internal.stream.buffer.AbstractProcessor;
29 import com.sun.xml.internal.stream.buffer.AttributesHolder;
30 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
31 import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark;
32 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx;
33 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
34 
35 import javax.xml.XMLConstants;
36 import javax.xml.namespace.QName;
37 import javax.xml.stream.Location;
38 import javax.xml.stream.XMLStreamException;
39 import javax.xml.stream.XMLStreamReader;
40 import java.util.*;
41 
42 /**
43  * A processor of a {@link XMLStreamBuffer} that reads the XML infoset as
44  * {@link XMLStreamReader}.
45  *
46  * <p>
47  * Because of {@link XMLStreamReader} design, this processor always produce
48  * a full document infoset, even if the buffer just contains a fragment.
49  *
50  * <p>
51  * When {@link XMLStreamBuffer} contains a multiple tree (AKA "forest"),
52  * {@link XMLStreamReader} will behave as if there are multiple root elements
53  * (so you'll see {@link #START_ELEMENT} event where you'd normally expect
54  * {@link #END_DOCUMENT}.)
55  *
56  * @author Paul.Sandoz@Sun.Com
57  * @author K.Venugopal@sun.com
58  */
59 public class StreamReaderBufferProcessor extends AbstractProcessor implements XMLStreamReaderEx {
60     private static final int CACHE_SIZE = 16;
61 
62     // Stack to hold element and namespace declaration information
63     protected ElementStackEntry[] _stack = new ElementStackEntry[CACHE_SIZE];
64     /** The top-most active entry of the {@link #_stack}. */
65     protected ElementStackEntry _stackTop;
66     /** The element depth that we are in. Used to determine when we are done with a tree. */
67     protected int _depth;
68 
69     // Arrays to hold all namespace declarations
70     /**
71      * Namespace prefixes. Can be empty but not null.
72      */
73     protected String[] _namespaceAIIsPrefix = new String[CACHE_SIZE];
74     protected String[] _namespaceAIIsNamespaceName = new String[CACHE_SIZE];
75     protected int _namespaceAIIsEnd;
76 
77     // Internal namespace context implementation
78     protected InternalNamespaceContext _nsCtx = new InternalNamespaceContext();
79 
80     // The current event type
81     protected int _eventType;
82 
83     /**
84      * Holder of the attributes.
85      *
86      * Be careful that this follows the SAX convention of using "" instead of null.
87      */
88     protected AttributesHolder _attributeCache;
89 
90     // Characters as a CharSequence
91     protected CharSequence _charSequence;
92 
93     // Characters as a char array with offset and length
94     protected char[] _characters;
95     protected int _textOffset;
96     protected int _textLen;
97 
98     protected String _piTarget;
99     protected String _piData;
100 
101     //
102     // Represents the parser state wrt the end of parsing.
103     //
104     /**
105      * The parser is in the middle of parsing a document,
106      * with no end in sight.
107      */
108     private static final int PARSING = 1;
109     /**
110      * The parser has already reported the {@link #END_ELEMENT},
111      * and we are parsing a fragment. We'll report {@link #END_DOCUMENT}
112      * next and be done.
113      */
114     private static final int PENDING_END_DOCUMENT = 2;
115     /**
116      * The parser has reported the {@link #END_DOCUMENT} event,
117      * so we are really done parsing.
118      */
119     private static final int COMPLETED = 3;
120 
121     /**
122      * True if processing is complete.
123      */
124     private int _completionState;
125 
StreamReaderBufferProcessor()126     public StreamReaderBufferProcessor() {
127         for (int i=0; i < _stack.length; i++){
128             _stack[i] = new ElementStackEntry();
129         }
130 
131         _attributeCache = new AttributesHolder();
132     }
133 
StreamReaderBufferProcessor(XMLStreamBuffer buffer)134     public StreamReaderBufferProcessor(XMLStreamBuffer buffer) throws XMLStreamException {
135         this();
136         setXMLStreamBuffer(buffer);
137     }
138 
setXMLStreamBuffer(XMLStreamBuffer buffer)139     public void setXMLStreamBuffer(XMLStreamBuffer buffer) throws XMLStreamException {
140         setBuffer(buffer,buffer.isFragment());
141 
142         _completionState = PARSING;
143         _namespaceAIIsEnd = 0;
144         _characters = null;
145         _charSequence = null;
146         _eventType = START_DOCUMENT;
147     }
148 
149     /**
150      * Does {@link #nextTag()} and if the parser moved to a new start tag,
151      * returns a {@link XMLStreamBufferMark} that captures the infoset starting
152      * from the newly discovered element.
153      *
154      * <p>
155      * (Ideally we should have a method that works against the current position,
156      * but the way the data structure is read makes this somewhat difficult.)
157      *
158      * This creates a new {@link XMLStreamBufferMark} that shares the underlying
159      * data storage, thus it's fairly efficient.
160      */
nextTagAndMark()161     public XMLStreamBuffer nextTagAndMark() throws XMLStreamException {
162         while (true) {
163             int s = peekStructure();
164             if((s &TYPE_MASK)==T_ELEMENT) {
165                 // next is start element.
166                 Map<String,String> inscope = new HashMap<String, String>(_namespaceAIIsEnd);
167 
168                 for (int i=0 ; i<_namespaceAIIsEnd; i++)
169                     inscope.put(_namespaceAIIsPrefix[i],_namespaceAIIsNamespaceName[i]);
170 
171                 XMLStreamBufferMark mark = new XMLStreamBufferMark(inscope, this);
172                 next();
173                 return mark;
174             } else if((s &TYPE_MASK)==T_DOCUMENT) {
175                 //move the pointer to next structure.
176                 readStructure();
177                 //mark the next start element
178                 XMLStreamBufferMark mark = new XMLStreamBufferMark(new HashMap<String, String>(_namespaceAIIsEnd), this);
179                 next();
180                 return mark;
181             }
182 
183             if(next()==END_ELEMENT)
184                 return null;
185         }
186     }
187 
getProperty(String name)188     public Object getProperty(String name) {
189         return null;
190     }
191 
next()192     public int next() throws XMLStreamException {
193         switch(_completionState) {
194             case COMPLETED:
195                 throw new XMLStreamException("Invalid State");
196             case PENDING_END_DOCUMENT:
197                 _namespaceAIIsEnd = 0;
198                 _completionState = COMPLETED;
199                 return _eventType = END_DOCUMENT;
200         }
201 
202         // Pop the stack of elements
203         // This is a post-processing operation
204         // The stack of the element should be poppoed after
205         // the END_ELEMENT event is returned so that the correct element name
206         // and namespace scope is returned
207         switch(_eventType) {
208             case END_ELEMENT:
209                 if (_depth > 1) {
210                     _depth--;
211                     // _depth index is always set to the next free stack entry
212                     // to push
213                     popElementStack(_depth);
214                 } else if (_depth == 1) {
215                     _depth--;
216                 }
217         }
218 
219         _characters = null;
220         _charSequence = null;
221         while(true) {// loop only if we read STATE_DOCUMENT
222             int eiiState = readEiiState();
223             switch(eiiState) {
224                 case STATE_DOCUMENT:
225                     // we'll always produce a full document, and we've already report START_DOCUMENT event.
226                     // so simply skil this
227                     continue;
228                 case STATE_ELEMENT_U_LN_QN: {
229                     final String uri = readStructureString();
230                     final String localName = readStructureString();
231                     final String prefix = getPrefixFromQName(readStructureString());
232 
233                     processElement(prefix, uri, localName, isInscope(_depth));
234                     return _eventType = START_ELEMENT;
235                 }
236                 case STATE_ELEMENT_P_U_LN:
237                     processElement(readStructureString(), readStructureString(), readStructureString(),isInscope(_depth));
238                     return _eventType = START_ELEMENT;
239                 case STATE_ELEMENT_U_LN:
240                     processElement(null, readStructureString(), readStructureString(),isInscope(_depth));
241                     return _eventType = START_ELEMENT;
242                 case STATE_ELEMENT_LN:
243                     processElement(null, null, readStructureString(),isInscope(_depth));
244                     return _eventType = START_ELEMENT;
245                 case STATE_TEXT_AS_CHAR_ARRAY_SMALL:
246                     _textLen = readStructure();
247                     _textOffset = readContentCharactersBuffer(_textLen);
248                     _characters = _contentCharactersBuffer;
249 
250                     return _eventType = CHARACTERS;
251                 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
252                     _textLen = readStructure16();
253                     _textOffset = readContentCharactersBuffer(_textLen);
254                     _characters = _contentCharactersBuffer;
255 
256                     return _eventType = CHARACTERS;
257                 case STATE_TEXT_AS_CHAR_ARRAY_COPY:
258                     _characters = readContentCharactersCopy();
259                     _textLen = _characters.length;
260                     _textOffset = 0;
261 
262                     return _eventType = CHARACTERS;
263                 case STATE_TEXT_AS_STRING:
264                     _eventType = CHARACTERS;
265                     _charSequence = readContentString();
266 
267                     return _eventType = CHARACTERS;
268                 case STATE_TEXT_AS_OBJECT:
269                     _eventType = CHARACTERS;
270                     _charSequence = (CharSequence)readContentObject();
271 
272                     return _eventType = CHARACTERS;
273                 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
274                     _textLen = readStructure();
275                     _textOffset = readContentCharactersBuffer(_textLen);
276                     _characters = _contentCharactersBuffer;
277 
278                     return _eventType = COMMENT;
279                 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
280                     _textLen = readStructure16();
281                     _textOffset = readContentCharactersBuffer(_textLen);
282                     _characters = _contentCharactersBuffer;
283 
284                     return _eventType = COMMENT;
285                 case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
286                     _characters = readContentCharactersCopy();
287                     _textLen = _characters.length;
288                     _textOffset = 0;
289 
290                     return _eventType = COMMENT;
291                 case STATE_COMMENT_AS_STRING:
292                     _charSequence = readContentString();
293 
294                     return _eventType = COMMENT;
295                 case STATE_PROCESSING_INSTRUCTION:
296                     _piTarget = readStructureString();
297                     _piData = readStructureString();
298 
299                     return _eventType = PROCESSING_INSTRUCTION;
300                 case STATE_END:
301                     if (_depth > 1) {
302                         // normal case
303                         return _eventType = END_ELEMENT;
304                     } else if (_depth == 1) {
305                         // this is the last end element for the current tree.
306                         if (_fragmentMode) {
307                             if(--_treeCount==0) // is this the last tree in the forest?
308                                 _completionState = PENDING_END_DOCUMENT;
309                         }
310                         return _eventType = END_ELEMENT;
311                     } else {
312                         // this only happens when we are processing a full document
313                         // and we hit the "end of document" marker
314                         _namespaceAIIsEnd = 0;
315                         _completionState = COMPLETED;
316                         return _eventType = END_DOCUMENT;
317                     }
318                 default:
319                     throw new XMLStreamException("Internal XSB error: Invalid State="+eiiState);
320             }
321             // this should be unreachable
322         }
323     }
324 
require(int type, String namespaceURI, String localName)325     public final void require(int type, String namespaceURI, String localName) throws XMLStreamException {
326         if( type != _eventType) {
327             throw new XMLStreamException("");
328         }
329         if( namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) {
330             throw new XMLStreamException("");
331         }
332         if(localName != null && !localName.equals(getLocalName())) {
333             throw new XMLStreamException("");
334         }
335     }
336 
getElementTextTrim()337     public final String getElementTextTrim() throws XMLStreamException {
338         // TODO getElementText* methods more efficiently
339         return getElementText().trim();
340     }
341 
getElementText()342     public final String getElementText() throws XMLStreamException {
343         if(_eventType != START_ELEMENT) {
344             throw new XMLStreamException("");
345         }
346 
347         next();
348         return getElementText(true);
349     }
350 
getElementText(boolean startElementRead)351     public final String getElementText(boolean startElementRead) throws XMLStreamException {
352         if (!startElementRead) {
353             throw new XMLStreamException("");
354         }
355 
356         int eventType = getEventType();
357         StringBuilder content = new StringBuilder();
358         while(eventType != END_ELEMENT ) {
359             if(eventType == CHARACTERS
360                     || eventType == CDATA
361                     || eventType == SPACE
362                     || eventType == ENTITY_REFERENCE) {
363                 content.append(getText());
364             } else if(eventType == PROCESSING_INSTRUCTION
365                     || eventType == COMMENT) {
366                 // skipping
367             } else if(eventType == END_DOCUMENT) {
368                 throw new XMLStreamException("");
369             } else if(eventType == START_ELEMENT) {
370                 throw new XMLStreamException("");
371             } else {
372                 throw new XMLStreamException("");
373             }
374             eventType = next();
375         }
376         return content.toString();
377     }
378 
nextTag()379     public final int nextTag() throws XMLStreamException {
380         next();
381         return nextTag(true);
382     }
383 
nextTag(boolean currentTagRead)384     public final int nextTag(boolean currentTagRead) throws XMLStreamException {
385         int eventType = getEventType();
386         if (!currentTagRead) {
387             eventType = next();
388         }
389         while((eventType == CHARACTERS && isWhiteSpace()) // skip whitespace
390         || (eventType == CDATA && isWhiteSpace())
391         || eventType == SPACE
392         || eventType == PROCESSING_INSTRUCTION
393         || eventType == COMMENT) {
394             eventType = next();
395         }
396         if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
397             throw new XMLStreamException("");
398         }
399         return eventType;
400     }
401 
hasNext()402     public final boolean hasNext() {
403         return (_eventType != END_DOCUMENT);
404     }
405 
close()406     public void close() throws XMLStreamException {
407     }
408 
isStartElement()409     public final boolean isStartElement() {
410         return (_eventType == START_ELEMENT);
411     }
412 
isEndElement()413     public final boolean isEndElement() {
414         return (_eventType == END_ELEMENT);
415     }
416 
isCharacters()417     public final boolean isCharacters() {
418         return (_eventType == CHARACTERS);
419     }
420 
isWhiteSpace()421     public final boolean isWhiteSpace() {
422         if(isCharacters() || (_eventType == CDATA)){
423             char [] ch = this.getTextCharacters();
424             int start = this.getTextStart();
425             int length = this.getTextLength();
426             for (int i = start; i < length; i++){
427                 final char c = ch[i];
428                 if (!(c == 0x20 || c == 0x9 || c == 0xD || c == 0xA))
429                     return false;
430             }
431             return true;
432         }
433         return false;
434     }
435 
getAttributeValue(String namespaceURI, String localName)436     public final String getAttributeValue(String namespaceURI, String localName) {
437         if (_eventType != START_ELEMENT) {
438             throw new IllegalStateException("");
439         }
440 
441         if (namespaceURI == null) {
442             // Set to the empty string to be compatible with the
443             // org.xml.sax.Attributes interface
444             namespaceURI = "";
445         }
446 
447         return _attributeCache.getValue(namespaceURI, localName);
448     }
449 
getAttributeCount()450     public final int getAttributeCount() {
451         if (_eventType != START_ELEMENT) {
452             throw new IllegalStateException("");
453         }
454 
455         return _attributeCache.getLength();
456     }
457 
getAttributeName(int index)458     public final javax.xml.namespace.QName getAttributeName(int index) {
459         if (_eventType != START_ELEMENT) {
460             throw new IllegalStateException("");
461         }
462 
463         final String prefix = _attributeCache.getPrefix(index);
464         final String localName = _attributeCache.getLocalName(index);
465         final String uri = _attributeCache.getURI(index);
466         return new QName(uri,localName,prefix);
467     }
468 
469 
getAttributeNamespace(int index)470     public final String getAttributeNamespace(int index) {
471         if (_eventType != START_ELEMENT) {
472             throw new IllegalStateException("");
473         }
474         return fixEmptyString(_attributeCache.getURI(index));
475     }
476 
getAttributeLocalName(int index)477     public final String getAttributeLocalName(int index) {
478         if (_eventType != START_ELEMENT) {
479             throw new IllegalStateException("");
480         }
481         return _attributeCache.getLocalName(index);
482     }
483 
getAttributePrefix(int index)484     public final String getAttributePrefix(int index) {
485         if (_eventType != START_ELEMENT) {
486             throw new IllegalStateException("");
487         }
488         return fixEmptyString(_attributeCache.getPrefix(index));
489     }
490 
getAttributeType(int index)491     public final String getAttributeType(int index) {
492         if (_eventType != START_ELEMENT) {
493             throw new IllegalStateException("");
494         }
495         return _attributeCache.getType(index);
496     }
497 
getAttributeValue(int index)498     public final String getAttributeValue(int index) {
499         if (_eventType != START_ELEMENT) {
500             throw new IllegalStateException("");
501         }
502 
503         return _attributeCache.getValue(index);
504     }
505 
isAttributeSpecified(int index)506     public final boolean isAttributeSpecified(int index) {
507         return false;
508     }
509 
getNamespaceCount()510     public final int getNamespaceCount() {
511         if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
512             return _stackTop.namespaceAIIsEnd - _stackTop.namespaceAIIsStart;
513         }
514 
515         throw new IllegalStateException("");
516     }
517 
getNamespacePrefix(int index)518     public final String getNamespacePrefix(int index) {
519         if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
520             return _namespaceAIIsPrefix[_stackTop.namespaceAIIsStart + index];
521         }
522 
523         throw new IllegalStateException("");
524     }
525 
getNamespaceURI(int index)526     public final String getNamespaceURI(int index) {
527         if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
528             return _namespaceAIIsNamespaceName[_stackTop.namespaceAIIsStart + index];
529         }
530 
531         throw new IllegalStateException("");
532     }
533 
getNamespaceURI(String prefix)534     public final String getNamespaceURI(String prefix) {
535         return _nsCtx.getNamespaceURI(prefix);
536     }
537 
getNamespaceContext()538     public final NamespaceContextEx getNamespaceContext() {
539         return _nsCtx;
540     }
541 
getEventType()542     public final int getEventType() {
543         return _eventType;
544     }
545 
getText()546     public final String getText() {
547         if (_characters != null) {
548             String s = new String(_characters, _textOffset, _textLen);
549             _charSequence = s;
550             return s;
551         } else if (_charSequence != null) {
552             return _charSequence.toString();
553         } else {
554             throw new IllegalStateException();
555         }
556     }
557 
getTextCharacters()558     public final char[] getTextCharacters() {
559         if (_characters != null) {
560             return _characters;
561         } else if (_charSequence != null) {
562             // TODO try to avoid creation of a temporary String for some
563             // CharSequence implementations
564             _characters = _charSequence.toString().toCharArray();
565             _textLen = _characters.length;
566             _textOffset = 0;
567             return _characters;
568         } else {
569             throw new IllegalStateException();
570         }
571     }
572 
getTextStart()573     public final int getTextStart() {
574         if (_characters != null) {
575             return _textOffset;
576         } else if (_charSequence != null) {
577             return 0;
578         } else {
579             throw new IllegalStateException();
580         }
581     }
582 
getTextLength()583     public final int getTextLength() {
584         if (_characters != null) {
585             return _textLen;
586         } else if (_charSequence != null) {
587             return _charSequence.length();
588         } else {
589             throw new IllegalStateException();
590         }
591     }
592 
getTextCharacters(int sourceStart, char[] target, int targetStart, int length)593     public final int getTextCharacters(int sourceStart, char[] target,
594                                        int targetStart, int length) throws XMLStreamException {
595         if (_characters != null) {
596         } else if (_charSequence != null) {
597             _characters = _charSequence.toString().toCharArray();
598             _textLen = _characters.length;
599             _textOffset = 0;
600         } else {
601             throw new IllegalStateException("");
602         }
603 
604         try {
605             int remaining = _textLen - sourceStart;
606             int len = remaining > length ? length : remaining;
607             sourceStart += _textOffset;
608             System.arraycopy(_characters, sourceStart, target, targetStart, len);
609             return len;
610         } catch (IndexOutOfBoundsException e) {
611             throw new XMLStreamException(e);
612         }
613     }
614 
615     private class CharSequenceImpl implements CharSequence {
616         private final int _offset;
617         private final int _length;
618 
CharSequenceImpl(int offset, int length)619         CharSequenceImpl(int offset, int length) {
620             _offset = offset;
621             _length = length;
622         }
623 
length()624         public int length() {
625             return _length;
626         }
627 
charAt(int index)628         public char charAt(int index) {
629             if (index >= 0 && index < _textLen) {
630                 return _characters[_textOffset + index];
631             } else {
632                 throw new IndexOutOfBoundsException();
633             }
634         }
635 
subSequence(int start, int end)636         public CharSequence subSequence(int start, int end) {
637             final int length = end - start;
638             if (end < 0 || start < 0 || end > length || start > end) {
639                 throw new IndexOutOfBoundsException();
640             }
641 
642             return new CharSequenceImpl(_offset + start, length);
643         }
644 
645         @Override
toString()646         public String toString() {
647             return new String(_characters, _offset, _length);
648         }
649     }
650 
getPCDATA()651     public final CharSequence getPCDATA() {
652         if (_characters != null) {
653             return new CharSequenceImpl(_textOffset, _textLen);
654         } else if (_charSequence != null) {
655             return _charSequence;
656         } else {
657             throw new IllegalStateException();
658         }
659     }
660 
getEncoding()661     public final String getEncoding() {
662         return "UTF-8";
663     }
664 
hasText()665     public final boolean hasText() {
666         return (_characters != null || _charSequence != null);
667     }
668 
getLocation()669     public final Location getLocation() {
670         return new DummyLocation();
671     }
672 
hasName()673     public final boolean hasName() {
674         return (_eventType == START_ELEMENT || _eventType == END_ELEMENT);
675     }
676 
getName()677     public final QName getName() {
678         return _stackTop.getQName();
679     }
680 
getLocalName()681     public final String getLocalName() {
682         return _stackTop.localName;
683     }
684 
getNamespaceURI()685     public final String getNamespaceURI() {
686         return _stackTop.uri;
687     }
688 
getPrefix()689     public final String getPrefix() {
690         return _stackTop.prefix;
691 
692     }
693 
getVersion()694     public final String getVersion() {
695         return "1.0";
696     }
697 
isStandalone()698     public final boolean isStandalone() {
699         return false;
700     }
701 
standaloneSet()702     public final boolean standaloneSet() {
703         return false;
704     }
705 
getCharacterEncodingScheme()706     public final String getCharacterEncodingScheme() {
707         return "UTF-8";
708     }
709 
getPITarget()710     public final String getPITarget() {
711         if (_eventType == PROCESSING_INSTRUCTION) {
712             return _piTarget;
713         }
714         throw new IllegalStateException("");
715     }
716 
getPIData()717     public final String getPIData() {
718         if (_eventType == PROCESSING_INSTRUCTION) {
719             return _piData;
720         }
721         throw new IllegalStateException("");
722     }
723 
processElement(String prefix, String uri, String localName, boolean inscope)724     protected void processElement(String prefix, String uri, String localName, boolean inscope) {
725         pushElementStack();
726         _stackTop.set(prefix, uri, localName);
727 
728         _attributeCache.clear();
729 
730         int item = peekStructure();
731         if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE || inscope) {
732             // Skip the namespace declarations on the element
733             // they will have been added already
734             item = processNamespaceAttributes(item, inscope);
735         }
736         if ((item & TYPE_MASK) == T_ATTRIBUTE) {
737             processAttributes(item);
738         }
739     }
740 
isInscope(int depth)741     private boolean isInscope(int depth) {
742         return _buffer.getInscopeNamespaces().size() > 0 && depth ==0;
743     }
744 
resizeNamespaceAttributes()745     private void resizeNamespaceAttributes() {
746         final String[] namespaceAIIsPrefix = new String[_namespaceAIIsEnd * 2];
747         System.arraycopy(_namespaceAIIsPrefix, 0, namespaceAIIsPrefix, 0, _namespaceAIIsEnd);
748         _namespaceAIIsPrefix = namespaceAIIsPrefix;
749 
750         final String[] namespaceAIIsNamespaceName = new String[_namespaceAIIsEnd * 2];
751         System.arraycopy(_namespaceAIIsNamespaceName, 0, namespaceAIIsNamespaceName, 0, _namespaceAIIsEnd);
752         _namespaceAIIsNamespaceName = namespaceAIIsNamespaceName;
753     }
754 
processNamespaceAttributes(int item, boolean inscope)755     private int processNamespaceAttributes(int item, boolean inscope){
756         _stackTop.namespaceAIIsStart = _namespaceAIIsEnd;
757         Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet();
758 
759         while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
760             if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) {
761                 resizeNamespaceAttributes();
762             }
763 
764             switch(getNIIState(item)){
765                 case STATE_NAMESPACE_ATTRIBUTE:
766                     // Undeclaration of default namespace
767                     _namespaceAIIsPrefix[_namespaceAIIsEnd] =
768                     _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = "";
769                     if (inscope) {
770                         prefixSet.add("");
771                     }
772                     break;
773                 case STATE_NAMESPACE_ATTRIBUTE_P:
774                     // Undeclaration of namespace
775                     _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString();
776                     if (inscope) {
777                         prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]);
778                     }
779                     _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = "";
780                     break;
781                 case STATE_NAMESPACE_ATTRIBUTE_P_U:
782                     // Declaration with prefix
783                     _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString();
784                     if (inscope) {
785                         prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]);
786                     }
787                     _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString();
788                     break;
789                 case STATE_NAMESPACE_ATTRIBUTE_U:
790                     // Default declaration
791                     _namespaceAIIsPrefix[_namespaceAIIsEnd] = "";
792                     if (inscope) {
793                         prefixSet.add("");
794                     }
795                     _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString();
796                     break;
797             }
798             readStructure();
799 
800             item = peekStructure();
801         }
802 
803         if (inscope) {
804             for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) {
805                 String key = fixNull(e.getKey());
806                 // If the prefix is already written, do not write the prefix
807                 if (!prefixSet.contains(key)) {
808                     if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) {
809                         resizeNamespaceAttributes();
810                     }
811                     _namespaceAIIsPrefix[_namespaceAIIsEnd] = key;
812                     _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = e.getValue();
813                 }
814             }
815         }
816         _stackTop.namespaceAIIsEnd = _namespaceAIIsEnd;
817 
818         return item;
819     }
820 
fixNull(String s)821     private static String fixNull(String s) {
822         if (s == null) return "";
823         else return s;
824     }
825 
processAttributes(int item)826     private void processAttributes(int item){
827         do {
828             switch(getAIIState(item)){
829                 case STATE_ATTRIBUTE_U_LN_QN: {
830                     final String uri = readStructureString();
831                     final String localName = readStructureString();
832                     final String prefix = getPrefixFromQName(readStructureString());
833                     _attributeCache.addAttributeWithPrefix(prefix, uri, localName, readStructureString(), readContentString());
834                     break;
835                 }
836                 case STATE_ATTRIBUTE_P_U_LN:
837                     _attributeCache.addAttributeWithPrefix(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString());
838                     break;
839                 case STATE_ATTRIBUTE_U_LN:
840                     // _attributeCache follows SAX convention
841                     _attributeCache.addAttributeWithPrefix("", readStructureString(), readStructureString(), readStructureString(), readContentString());
842                     break;
843                 case STATE_ATTRIBUTE_LN: {
844                     _attributeCache.addAttributeWithPrefix("", "", readStructureString(), readStructureString(), readContentString());
845                     break;
846                 }
847                 default :
848                     assert false : "Internal XSB Error: wrong attribute state, Item="+item;
849             }
850             readStructure();
851 
852             item = peekStructure();
853         } while((item & TYPE_MASK) == T_ATTRIBUTE);
854     }
855 
pushElementStack()856     private void pushElementStack() {
857         if (_depth == _stack.length) {
858             // resize stack
859             ElementStackEntry [] tmp = _stack;
860             _stack = new ElementStackEntry[_stack.length * 3 /2 + 1];
861             System.arraycopy(tmp, 0, _stack, 0, tmp.length);
862             for (int i = tmp.length; i < _stack.length; i++){
863                 _stack[i] = new ElementStackEntry();
864             }
865         }
866 
867         _stackTop = _stack[_depth++];
868     }
869 
popElementStack(int depth)870     private void popElementStack(int depth) {
871         // _depth is checked outside this method
872         _stackTop = _stack[depth - 1];
873         // Move back the position of the namespace index
874         _namespaceAIIsEnd = _stack[depth].namespaceAIIsStart;
875     }
876 
877     private final class ElementStackEntry {
878         /**
879          * Prefix.
880          * Just like everywhere else in StAX, this can be null but can't be empty.
881          */
882         String prefix;
883         /**
884          * Namespace URI.
885          * Just like everywhere else in StAX, this can be null but can't be empty.
886          */
887         String uri;
888         String localName;
889         QName qname;
890 
891         // Start and end of namespace declarations
892         // in namespace declaration arrays
893         int namespaceAIIsStart;
894         int namespaceAIIsEnd;
895 
set(String prefix, String uri, String localName)896         public void set(String prefix, String uri, String localName) {
897             this.prefix = prefix;
898             this.uri = uri;
899             this.localName = localName;
900             this.qname = null;
901 
902             this.namespaceAIIsStart = this.namespaceAIIsEnd = StreamReaderBufferProcessor.this._namespaceAIIsEnd;
903         }
904 
getQName()905         public QName getQName() {
906             if (qname == null) {
907                 qname = new QName(fixNull(uri), localName, fixNull(prefix));
908             }
909             return qname;
910         }
911 
fixNull(String s)912         private String fixNull(String s) {
913             return (s == null) ? "" : s;
914         }
915     }
916 
917     private final class InternalNamespaceContext implements NamespaceContextEx {
918         @SuppressWarnings({"StringEquality"})
getNamespaceURI(String prefix)919         public String getNamespaceURI(String prefix) {
920             if (prefix == null) {
921                 throw new IllegalArgumentException("Prefix cannot be null");
922             }
923 
924             /*
925              * If the buffer was created using string interning
926              * intern the prefix and check for reference equality
927              * rather than using String.equals();
928              */
929             if (_stringInterningFeature) {
930                 prefix = prefix.intern();
931 
932                 // Find the most recently declared prefix
933                 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) {
934                     if (prefix == _namespaceAIIsPrefix[i]) {
935                         return _namespaceAIIsNamespaceName[i];
936                     }
937                 }
938             } else {
939                 // Find the most recently declared prefix
940                 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) {
941                     if (prefix.equals(_namespaceAIIsPrefix[i])) {
942                         return _namespaceAIIsNamespaceName[i];
943                     }
944                 }
945             }
946 
947             // Check for XML-based prefixes
948             if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
949                 return XMLConstants.XML_NS_URI;
950             } else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
951                 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
952             }
953 
954             return null;
955         }
956 
getPrefix(String namespaceURI)957         public String getPrefix(String namespaceURI) {
958             final Iterator i = getPrefixes(namespaceURI);
959             if (i.hasNext()) {
960                 return (String)i.next();
961             } else {
962                 return null;
963             }
964         }
965 
getPrefixes(final String namespaceURI)966         public Iterator getPrefixes(final String namespaceURI) {
967             if (namespaceURI == null){
968                 throw new IllegalArgumentException("NamespaceURI cannot be null");
969             }
970 
971             if (namespaceURI.equals(XMLConstants.XML_NS_URI)) {
972                 return Collections.singletonList(XMLConstants.XML_NS_PREFIX).iterator();
973             } else if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
974                 return Collections.singletonList(XMLConstants.XMLNS_ATTRIBUTE).iterator();
975             }
976 
977             return new Iterator() {
978                 private int i = _namespaceAIIsEnd - 1;
979                 private boolean requireFindNext = true;
980                 private String p;
981 
982                 private String findNext() {
983                     while(i >= 0) {
984                         // Find the most recently declared namespace
985                         if (namespaceURI.equals(_namespaceAIIsNamespaceName[i])) {
986                             // Find the most recently declared prefix of the namespace
987                             // and check if the prefix is in scope with that namespace
988                             if (getNamespaceURI(_namespaceAIIsPrefix[i]).equals(
989                                     _namespaceAIIsNamespaceName[i])) {
990                                 return p = _namespaceAIIsPrefix[i];
991                             }
992                         }
993                         i--;
994                     }
995                     return p = null;
996                 }
997 
998                 public boolean hasNext() {
999                     if (requireFindNext) {
1000                         findNext();
1001                         requireFindNext = false;
1002                     }
1003                     return (p != null);
1004                 }
1005 
1006                 public Object next() {
1007                     if (requireFindNext) {
1008                         findNext();
1009                     }
1010                     requireFindNext = true;
1011 
1012                     if (p == null) {
1013                         throw new NoSuchElementException();
1014                     }
1015 
1016                     return p;
1017                 }
1018 
1019                 public void remove() {
1020                     throw new UnsupportedOperationException();
1021                 }
1022             };
1023         }
1024 
1025         private class BindingImpl implements NamespaceContextEx.Binding {
1026             final String _prefix;
1027             final String _namespaceURI;
1028 
BindingImpl(String prefix, String namespaceURI)1029             BindingImpl(String prefix, String namespaceURI) {
1030                 _prefix = prefix;
1031                 _namespaceURI = namespaceURI;
1032             }
1033 
getPrefix()1034             public String getPrefix() {
1035                 return _prefix;
1036             }
1037 
getNamespaceURI()1038             public String getNamespaceURI() {
1039                 return _namespaceURI;
1040             }
1041         }
1042 
iterator()1043         public Iterator<NamespaceContextEx.Binding> iterator() {
1044             return new Iterator<NamespaceContextEx.Binding>() {
1045                 private final int end = _namespaceAIIsEnd - 1;
1046                 private int current = end;
1047                 private boolean requireFindNext = true;
1048                 private NamespaceContextEx.Binding namespace;
1049 
1050                 private NamespaceContextEx.Binding findNext() {
1051                     while(current >= 0) {
1052                         final String prefix = _namespaceAIIsPrefix[current];
1053 
1054                         // Find if the current prefix occurs more recently
1055                         // If so then it is not in scope
1056                         int i = end;
1057                         for (;i > current; i--) {
1058                             if (prefix.equals(_namespaceAIIsPrefix[i])) {
1059                                 break;
1060                             }
1061                         }
1062                         if (i == current--) {
1063                             // The current prefix is in-scope
1064                             return namespace = new BindingImpl(prefix, _namespaceAIIsNamespaceName[current]);
1065                         }
1066                     }
1067                     return namespace = null;
1068                 }
1069 
1070                 public boolean hasNext() {
1071                     if (requireFindNext) {
1072                         findNext();
1073                         requireFindNext = false;
1074                     }
1075                     return (namespace != null);
1076                 }
1077 
1078                 public NamespaceContextEx.Binding next() {
1079                     if (requireFindNext) {
1080                         findNext();
1081                     }
1082                     requireFindNext = true;
1083 
1084                     if (namespace == null) {
1085                         throw new NoSuchElementException();
1086                     }
1087 
1088                     return namespace;
1089                 }
1090 
1091                 public void remove() {
1092                     throw new UnsupportedOperationException();
1093                 }
1094             };
1095         }
1096     }
1097 
1098     private class DummyLocation  implements Location {
1099         public int getLineNumber() {
1100             return -1;
1101         }
1102 
1103         public int getColumnNumber() {
1104             return -1;
1105         }
1106 
1107         public int getCharacterOffset() {
1108             return -1;
1109         }
1110 
1111         public String getPublicId() {
1112             return null;
1113         }
1114 
1115         public String getSystemId() {
1116             return _buffer.getSystemId();
1117         }
1118     }
1119 
1120     private static String fixEmptyString(String s) {
1121         // s must not be null, so no need to check for that. that would be bug.
1122         if(s.length()==0)   return null;
1123         else                return s;
1124     }
1125 
1126 }
1127