Thu, 31 Aug 2017 15:18:52 +0800
merge
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 */
26 package com.sun.xml.internal.stream.buffer.stax;
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;
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.*;
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;
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;
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;
77 // Internal namespace context implementation
78 protected InternalNamespaceContext _nsCtx = new InternalNamespaceContext();
80 // The current event type
81 protected int _eventType;
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;
90 // Characters as a CharSequence
91 protected CharSequence _charSequence;
93 // Characters as a char array with offset and length
94 protected char[] _characters;
95 protected int _textOffset;
96 protected int _textLen;
98 protected String _piTarget;
99 protected String _piData;
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;
121 /**
122 * True if processing is complete.
123 */
124 private int _completionState;
126 public StreamReaderBufferProcessor() {
127 for (int i=0; i < _stack.length; i++){
128 _stack[i] = new ElementStackEntry();
129 }
131 _attributeCache = new AttributesHolder();
132 }
134 public StreamReaderBufferProcessor(XMLStreamBuffer buffer) throws XMLStreamException {
135 this();
136 setXMLStreamBuffer(buffer);
137 }
139 public void setXMLStreamBuffer(XMLStreamBuffer buffer) throws XMLStreamException {
140 setBuffer(buffer,buffer.isFragment());
142 _completionState = PARSING;
143 _namespaceAIIsEnd = 0;
144 _characters = null;
145 _charSequence = null;
146 _eventType = START_DOCUMENT;
147 }
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 */
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);
168 for (int i=0 ; i<_namespaceAIIsEnd; i++)
169 inscope.put(_namespaceAIIsPrefix[i],_namespaceAIIsNamespaceName[i]);
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 }
183 if(next()==END_ELEMENT)
184 return null;
185 }
186 }
188 public Object getProperty(String name) {
189 return null;
190 }
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 }
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 }
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());
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;
250 return _eventType = CHARACTERS;
251 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
252 _textLen = readStructure16();
253 _textOffset = readContentCharactersBuffer(_textLen);
254 _characters = _contentCharactersBuffer;
256 return _eventType = CHARACTERS;
257 case STATE_TEXT_AS_CHAR_ARRAY_COPY:
258 _characters = readContentCharactersCopy();
259 _textLen = _characters.length;
260 _textOffset = 0;
262 return _eventType = CHARACTERS;
263 case STATE_TEXT_AS_STRING:
264 _eventType = CHARACTERS;
265 _charSequence = readContentString();
267 return _eventType = CHARACTERS;
268 case STATE_TEXT_AS_OBJECT:
269 _eventType = CHARACTERS;
270 _charSequence = (CharSequence)readContentObject();
272 return _eventType = CHARACTERS;
273 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
274 _textLen = readStructure();
275 _textOffset = readContentCharactersBuffer(_textLen);
276 _characters = _contentCharactersBuffer;
278 return _eventType = COMMENT;
279 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
280 _textLen = readStructure16();
281 _textOffset = readContentCharactersBuffer(_textLen);
282 _characters = _contentCharactersBuffer;
284 return _eventType = COMMENT;
285 case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
286 _characters = readContentCharactersCopy();
287 _textLen = _characters.length;
288 _textOffset = 0;
290 return _eventType = COMMENT;
291 case STATE_COMMENT_AS_STRING:
292 _charSequence = readContentString();
294 return _eventType = COMMENT;
295 case STATE_PROCESSING_INSTRUCTION:
296 _piTarget = readStructureString();
297 _piData = readStructureString();
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 }
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 }
337 public final String getElementTextTrim() throws XMLStreamException {
338 // TODO getElementText* methods more efficiently
339 return getElementText().trim();
340 }
342 public final String getElementText() throws XMLStreamException {
343 if(_eventType != START_ELEMENT) {
344 throw new XMLStreamException("");
345 }
347 next();
348 return getElementText(true);
349 }
351 public final String getElementText(boolean startElementRead) throws XMLStreamException {
352 if (!startElementRead) {
353 throw new XMLStreamException("");
354 }
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 }
379 public final int nextTag() throws XMLStreamException {
380 next();
381 return nextTag(true);
382 }
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 }
402 public final boolean hasNext() {
403 return (_eventType != END_DOCUMENT);
404 }
406 public void close() throws XMLStreamException {
407 }
409 public final boolean isStartElement() {
410 return (_eventType == START_ELEMENT);
411 }
413 public final boolean isEndElement() {
414 return (_eventType == END_ELEMENT);
415 }
417 public final boolean isCharacters() {
418 return (_eventType == CHARACTERS);
419 }
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 }
436 public final String getAttributeValue(String namespaceURI, String localName) {
437 if (_eventType != START_ELEMENT) {
438 throw new IllegalStateException("");
439 }
441 if (namespaceURI == null) {
442 // Set to the empty string to be compatible with the
443 // org.xml.sax.Attributes interface
444 namespaceURI = "";
445 }
447 return _attributeCache.getValue(namespaceURI, localName);
448 }
450 public final int getAttributeCount() {
451 if (_eventType != START_ELEMENT) {
452 throw new IllegalStateException("");
453 }
455 return _attributeCache.getLength();
456 }
458 public final javax.xml.namespace.QName getAttributeName(int index) {
459 if (_eventType != START_ELEMENT) {
460 throw new IllegalStateException("");
461 }
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 }
470 public final String getAttributeNamespace(int index) {
471 if (_eventType != START_ELEMENT) {
472 throw new IllegalStateException("");
473 }
474 return fixEmptyString(_attributeCache.getURI(index));
475 }
477 public final String getAttributeLocalName(int index) {
478 if (_eventType != START_ELEMENT) {
479 throw new IllegalStateException("");
480 }
481 return _attributeCache.getLocalName(index);
482 }
484 public final String getAttributePrefix(int index) {
485 if (_eventType != START_ELEMENT) {
486 throw new IllegalStateException("");
487 }
488 return fixEmptyString(_attributeCache.getPrefix(index));
489 }
491 public final String getAttributeType(int index) {
492 if (_eventType != START_ELEMENT) {
493 throw new IllegalStateException("");
494 }
495 return _attributeCache.getType(index);
496 }
498 public final String getAttributeValue(int index) {
499 if (_eventType != START_ELEMENT) {
500 throw new IllegalStateException("");
501 }
503 return _attributeCache.getValue(index);
504 }
506 public final boolean isAttributeSpecified(int index) {
507 return false;
508 }
510 public final int getNamespaceCount() {
511 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
512 return _stackTop.namespaceAIIsEnd - _stackTop.namespaceAIIsStart;
513 }
515 throw new IllegalStateException("");
516 }
518 public final String getNamespacePrefix(int index) {
519 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
520 return _namespaceAIIsPrefix[_stackTop.namespaceAIIsStart + index];
521 }
523 throw new IllegalStateException("");
524 }
526 public final String getNamespaceURI(int index) {
527 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
528 return _namespaceAIIsNamespaceName[_stackTop.namespaceAIIsStart + index];
529 }
531 throw new IllegalStateException("");
532 }
534 public final String getNamespaceURI(String prefix) {
535 return _nsCtx.getNamespaceURI(prefix);
536 }
538 public final NamespaceContextEx getNamespaceContext() {
539 return _nsCtx;
540 }
542 public final int getEventType() {
543 return _eventType;
544 }
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 }
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 }
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 }
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 }
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 }
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 }
615 private class CharSequenceImpl implements CharSequence {
616 private final int _offset;
617 private final int _length;
619 CharSequenceImpl(int offset, int length) {
620 _offset = offset;
621 _length = length;
622 }
624 public int length() {
625 return _length;
626 }
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 }
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 }
642 return new CharSequenceImpl(_offset + start, length);
643 }
645 @Override
646 public String toString() {
647 return new String(_characters, _offset, _length);
648 }
649 }
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 }
661 public final String getEncoding() {
662 return "UTF-8";
663 }
665 public final boolean hasText() {
666 return (_characters != null || _charSequence != null);
667 }
669 public final Location getLocation() {
670 return new DummyLocation();
671 }
673 public final boolean hasName() {
674 return (_eventType == START_ELEMENT || _eventType == END_ELEMENT);
675 }
677 public final QName getName() {
678 return _stackTop.getQName();
679 }
681 public final String getLocalName() {
682 return _stackTop.localName;
683 }
685 public final String getNamespaceURI() {
686 return _stackTop.uri;
687 }
689 public final String getPrefix() {
690 return _stackTop.prefix;
692 }
694 public final String getVersion() {
695 return "1.0";
696 }
698 public final boolean isStandalone() {
699 return false;
700 }
702 public final boolean standaloneSet() {
703 return false;
704 }
706 public final String getCharacterEncodingScheme() {
707 return "UTF-8";
708 }
710 public final String getPITarget() {
711 if (_eventType == PROCESSING_INSTRUCTION) {
712 return _piTarget;
713 }
714 throw new IllegalStateException("");
715 }
717 public final String getPIData() {
718 if (_eventType == PROCESSING_INSTRUCTION) {
719 return _piData;
720 }
721 throw new IllegalStateException("");
722 }
724 protected void processElement(String prefix, String uri, String localName, boolean inscope) {
725 pushElementStack();
726 _stackTop.set(prefix, uri, localName);
728 _attributeCache.clear();
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 }
741 private boolean isInscope(int depth) {
742 return _buffer.getInscopeNamespaces().size() > 0 && depth ==0;
743 }
745 private void resizeNamespaceAttributes() {
746 final String[] namespaceAIIsPrefix = new String[_namespaceAIIsEnd * 2];
747 System.arraycopy(_namespaceAIIsPrefix, 0, namespaceAIIsPrefix, 0, _namespaceAIIsEnd);
748 _namespaceAIIsPrefix = namespaceAIIsPrefix;
750 final String[] namespaceAIIsNamespaceName = new String[_namespaceAIIsEnd * 2];
751 System.arraycopy(_namespaceAIIsNamespaceName, 0, namespaceAIIsNamespaceName, 0, _namespaceAIIsEnd);
752 _namespaceAIIsNamespaceName = namespaceAIIsNamespaceName;
753 }
755 private int processNamespaceAttributes(int item, boolean inscope){
756 _stackTop.namespaceAIIsStart = _namespaceAIIsEnd;
757 Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet();
759 while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
760 if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) {
761 resizeNamespaceAttributes();
762 }
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();
800 item = peekStructure();
801 }
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;
818 return item;
819 }
821 private static String fixNull(String s) {
822 if (s == null) return "";
823 else return s;
824 }
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();
852 item = peekStructure();
853 } while((item & TYPE_MASK) == T_ATTRIBUTE);
854 }
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 }
867 _stackTop = _stack[_depth++];
868 }
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 }
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;
891 // Start and end of namespace declarations
892 // in namespace declaration arrays
893 int namespaceAIIsStart;
894 int namespaceAIIsEnd;
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;
902 this.namespaceAIIsStart = this.namespaceAIIsEnd = StreamReaderBufferProcessor.this._namespaceAIIsEnd;
903 }
905 public QName getQName() {
906 if (qname == null) {
907 qname = new QName(fixNull(uri), localName, fixNull(prefix));
908 }
909 return qname;
910 }
912 private String fixNull(String s) {
913 return (s == null) ? "" : s;
914 }
915 }
917 private final class InternalNamespaceContext implements NamespaceContextEx {
918 @SuppressWarnings({"StringEquality"})
919 public String getNamespaceURI(String prefix) {
920 if (prefix == null) {
921 throw new IllegalArgumentException("Prefix cannot be null");
922 }
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();
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 }
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 }
954 return null;
955 }
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 }
966 public Iterator getPrefixes(final String namespaceURI) {
967 if (namespaceURI == null){
968 throw new IllegalArgumentException("NamespaceURI cannot be null");
969 }
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 }
977 return new Iterator() {
978 private int i = _namespaceAIIsEnd - 1;
979 private boolean requireFindNext = true;
980 private String p;
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 }
998 public boolean hasNext() {
999 if (requireFindNext) {
1000 findNext();
1001 requireFindNext = false;
1002 }
1003 return (p != null);
1004 }
1006 public Object next() {
1007 if (requireFindNext) {
1008 findNext();
1009 }
1010 requireFindNext = true;
1012 if (p == null) {
1013 throw new NoSuchElementException();
1014 }
1016 return p;
1017 }
1019 public void remove() {
1020 throw new UnsupportedOperationException();
1021 }
1022 };
1023 }
1025 private class BindingImpl implements NamespaceContextEx.Binding {
1026 final String _prefix;
1027 final String _namespaceURI;
1029 BindingImpl(String prefix, String namespaceURI) {
1030 _prefix = prefix;
1031 _namespaceURI = namespaceURI;
1032 }
1034 public String getPrefix() {
1035 return _prefix;
1036 }
1038 public String getNamespaceURI() {
1039 return _namespaceURI;
1040 }
1041 }
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;
1050 private NamespaceContextEx.Binding findNext() {
1051 while(current >= 0) {
1052 final String prefix = _namespaceAIIsPrefix[current];
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 }
1070 public boolean hasNext() {
1071 if (requireFindNext) {
1072 findNext();
1073 requireFindNext = false;
1074 }
1075 return (namespace != null);
1076 }
1078 public NamespaceContextEx.Binding next() {
1079 if (requireFindNext) {
1080 findNext();
1081 }
1082 requireFindNext = true;
1084 if (namespace == null) {
1085 throw new NoSuchElementException();
1086 }
1088 return namespace;
1089 }
1091 public void remove() {
1092 throw new UnsupportedOperationException();
1093 }
1094 };
1095 }
1096 }
1098 private class DummyLocation implements Location {
1099 public int getLineNumber() {
1100 return -1;
1101 }
1103 public int getColumnNumber() {
1104 return -1;
1105 }
1107 public int getCharacterOffset() {
1108 return -1;
1109 }
1111 public String getPublicId() {
1112 return null;
1113 }
1115 public String getSystemId() {
1116 return _buffer.getSystemId();
1117 }
1118 }
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 }
1126 }