0001: /*
0002: * The contents of this file are subject to the terms
0003: * of the Common Development and Distribution License
0004: * (the "License"). You may not use this file except
0005: * in compliance with the License.
0006: *
0007: * You can obtain a copy of the license at
0008: * https://jwsdp.dev.java.net/CDDLv1.0.html
0009: * See the License for the specific language governing
0010: * permissions and limitations under the License.
0011: *
0012: * When distributing Covered Code, include this CDDL
0013: * HEADER in each file and include the License file at
0014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
0015: * add the following below this CDDL HEADER, with the
0016: * fields enclosed by brackets "[]" replaced with your
0017: * own identifying information: Portions Copyright [yyyy]
0018: * [name of copyright owner]
0019: */
0020: package com.sun.xml.stream.buffer.stax;
0021:
0022: import com.sun.xml.stream.buffer.AbstractProcessor;
0023: import com.sun.xml.stream.buffer.AttributesHolder;
0024: import com.sun.xml.stream.buffer.XMLStreamBuffer;
0025: import com.sun.xml.stream.buffer.XMLStreamBufferMark;
0026: import org.jvnet.staxex.NamespaceContextEx;
0027: import org.jvnet.staxex.XMLStreamReaderEx;
0028:
0029: import javax.xml.XMLConstants;
0030: import javax.xml.namespace.QName;
0031: import javax.xml.stream.Location;
0032: import javax.xml.stream.XMLStreamException;
0033: import javax.xml.stream.XMLStreamReader;
0034: import java.util.Collections;
0035: import java.util.HashMap;
0036: import java.util.Iterator;
0037: import java.util.Map;
0038: import java.util.NoSuchElementException;
0039:
0040: /**
0041: * A processor of a {@link XMLStreamBuffer} that reads the XML infoset as
0042: * {@link XMLStreamReader}.
0043: *
0044: * <p>
0045: * Because of {@link XMLStreamReader} design, this processor always produce
0046: * a full document infoset, even if the buffer just contains a fragment.
0047: *
0048: * <p>
0049: * When {@link XMLStreamBuffer} contains a multiple tree (AKA "forest"),
0050: * {@link XMLStreamReader} will behave as if there are multiple root elements
0051: * (so you'll see {@link #START_ELEMENT} event where you'd normally expect
0052: * {@link #END_DOCUMENT}.)
0053: *
0054: * @author Paul.Sandoz@Sun.Com
0055: * @author K.Venugopal@sun.com
0056: */
0057: public class StreamReaderBufferProcessor extends AbstractProcessor
0058: implements XMLStreamReaderEx {
0059: private static final int CACHE_SIZE = 16;
0060:
0061: // Stack to hold element and namespace declaration information
0062: protected ElementStackEntry[] _stack = new ElementStackEntry[CACHE_SIZE];
0063: /** The top-most active entry of the {@link #_stack}. */
0064: protected ElementStackEntry _stackTop;
0065: /** The element depth that we are in. Used to determine when we are done with a tree. */
0066: protected int _depth;
0067:
0068: // Arrays to hold all namespace declarations
0069: /**
0070: * Namespace prefixes. Can be empty but not null.
0071: */
0072: protected String[] _namespaceAIIsPrefix = new String[CACHE_SIZE];
0073: protected String[] _namespaceAIIsNamespaceName = new String[CACHE_SIZE];
0074: protected int _namespaceAIIsEnd;
0075:
0076: // Internal namespace context implementation
0077: protected InternalNamespaceContext _nsCtx = new InternalNamespaceContext();
0078:
0079: // The current event type
0080: protected int _eventType;
0081:
0082: /**
0083: * Holder of the attributes.
0084: *
0085: * Be careful that this follows the SAX convention of using "" instead of null.
0086: */
0087: protected AttributesHolder _attributeCache;
0088:
0089: // Characters as a CharSequence
0090: protected CharSequence _charSequence;
0091:
0092: // Characters as a char array with offset and length
0093: protected char[] _characters;
0094: protected int _textOffset;
0095: protected int _textLen;
0096:
0097: protected String _piTarget;
0098: protected String _piData;
0099:
0100: //
0101: // Represents the parser state wrt the end of parsing.
0102: //
0103: /**
0104: * The parser is in the middle of parsing a document,
0105: * with no end in sight.
0106: */
0107: private static final int PARSING = 1;
0108: /**
0109: * The parser has already reported the {@link #END_ELEMENT},
0110: * and we are parsing a fragment. We'll report {@link #END_DOCUMENT}
0111: * next and be done.
0112: */
0113: private static final int PENDING_END_DOCUMENT = 2;
0114: /**
0115: * The parser has reported the {@link #END_DOCUMENT} event,
0116: * so we are really done parsing.
0117: */
0118: private static final int COMPLETED = 3;
0119:
0120: /**
0121: * True if processing is complete.
0122: */
0123: private int _completionState;
0124:
0125: public StreamReaderBufferProcessor() {
0126: for (int i = 0; i < _stack.length; i++) {
0127: _stack[i] = new ElementStackEntry();
0128: }
0129:
0130: _attributeCache = new AttributesHolder();
0131: }
0132:
0133: public StreamReaderBufferProcessor(XMLStreamBuffer buffer)
0134: throws XMLStreamException {
0135: this ();
0136: setXMLStreamBuffer(buffer);
0137: }
0138:
0139: public void setXMLStreamBuffer(XMLStreamBuffer buffer)
0140: throws XMLStreamException {
0141: setBuffer(buffer, buffer.isFragment());
0142:
0143: _completionState = PARSING;
0144: _namespaceAIIsEnd = 0;
0145: _characters = null;
0146: _charSequence = null;
0147: _eventType = START_DOCUMENT;
0148: }
0149:
0150: /**
0151: * Does {@link #nextTag()} and if the parser moved to a new start tag,
0152: * returns a {@link XMLStreamBufferMark} that captures the infoset starting
0153: * from the newly discovered element.
0154: *
0155: * <p>
0156: * (Ideally we should have a method that works against the current position,
0157: * but the way the data structure is read makes this somewhat difficult.)
0158: *
0159: * This creates a new {@link XMLStreamBufferMark} that shares the underlying
0160: * data storage, thus it's fairly efficient.
0161: */
0162: public XMLStreamBuffer nextTagAndMark() throws XMLStreamException {
0163: while (true) {
0164: int s = peekStructure();
0165: if ((s & TYPE_MASK) == T_ELEMENT) {
0166: // next is start element.
0167: Map<String, String> inscope = new HashMap<String, String>(
0168: _namespaceAIIsEnd);
0169:
0170: for (int i = 0; i < _namespaceAIIsEnd; i++)
0171: inscope.put(_namespaceAIIsPrefix[i],
0172: _namespaceAIIsNamespaceName[i]);
0173:
0174: XMLStreamBufferMark mark = new XMLStreamBufferMark(
0175: inscope, this );
0176: next();
0177: return mark;
0178: }
0179:
0180: if (next() == END_ELEMENT)
0181: return null;
0182: }
0183: }
0184:
0185: public Object getProperty(String name) {
0186: return null;
0187: }
0188:
0189: public int next() throws XMLStreamException {
0190: switch (_completionState) {
0191: case COMPLETED:
0192: throw new XMLStreamException("Invalid State");
0193: case PENDING_END_DOCUMENT:
0194: _namespaceAIIsEnd = 0;
0195: _completionState = COMPLETED;
0196: return _eventType = END_DOCUMENT;
0197: }
0198:
0199: // Pop the stack of elements
0200: // This is a post-processing operation
0201: // The stack of the element should be poppoed after
0202: // the END_ELEMENT event is returned so that the correct element name
0203: // and namespace scope is returned
0204: switch (_eventType) {
0205: case END_ELEMENT:
0206: if (_depth > 1) {
0207: _depth--;
0208: // _depth index is always set to the next free stack entry
0209: // to push
0210: popElementStack(_depth);
0211: } else if (_depth == 1) {
0212: _depth--;
0213: }
0214: }
0215:
0216: _characters = null;
0217: _charSequence = null;
0218: while (true) {// loop only if we read STATE_DOCUMENT
0219: switch (readEiiState()) {
0220: case STATE_DOCUMENT:
0221: // we'll always produce a full document, and we've already report START_DOCUMENT event.
0222: // so simply skil this
0223: continue;
0224: case STATE_ELEMENT_U_LN_QN: {
0225: final String uri = readStructureString();
0226: final String localName = readStructureString();
0227: final String prefix = getPrefixFromQName(readStructureString());
0228:
0229: processElement(prefix, uri, localName);
0230: return _eventType = START_ELEMENT;
0231: }
0232: case STATE_ELEMENT_P_U_LN:
0233: processElement(readStructureString(),
0234: readStructureString(), readStructureString());
0235: return _eventType = START_ELEMENT;
0236: case STATE_ELEMENT_U_LN:
0237: processElement(null, readStructureString(),
0238: readStructureString());
0239: return _eventType = START_ELEMENT;
0240: case STATE_ELEMENT_LN:
0241: processElement(null, null, readStructureString());
0242: return _eventType = START_ELEMENT;
0243: case STATE_TEXT_AS_CHAR_ARRAY_SMALL:
0244: _textLen = readStructure();
0245: _textOffset = readContentCharactersBuffer(_textLen);
0246: _characters = _contentCharactersBuffer;
0247:
0248: return _eventType = CHARACTERS;
0249: case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM:
0250: _textLen = readStructure16();
0251: _textOffset = readContentCharactersBuffer(_textLen);
0252: _characters = _contentCharactersBuffer;
0253:
0254: return _eventType = CHARACTERS;
0255: case STATE_TEXT_AS_CHAR_ARRAY_COPY:
0256: _characters = readContentCharactersCopy();
0257: _textLen = _characters.length;
0258: _textOffset = 0;
0259:
0260: return _eventType = CHARACTERS;
0261: case STATE_TEXT_AS_STRING:
0262: _eventType = CHARACTERS;
0263: _charSequence = readContentString();
0264:
0265: return _eventType = CHARACTERS;
0266: case STATE_TEXT_AS_OBJECT:
0267: _eventType = CHARACTERS;
0268: _charSequence = (CharSequence) readContentObject();
0269:
0270: return _eventType = CHARACTERS;
0271: case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
0272: _textLen = readStructure();
0273: _textOffset = readContentCharactersBuffer(_textLen);
0274: _characters = _contentCharactersBuffer;
0275:
0276: return _eventType = COMMENT;
0277: case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
0278: _textLen = readStructure16();
0279: _textOffset = readContentCharactersBuffer(_textLen);
0280: _characters = _contentCharactersBuffer;
0281:
0282: return _eventType = COMMENT;
0283: case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
0284: _characters = readContentCharactersCopy();
0285: _textLen = _characters.length;
0286: _textOffset = 0;
0287:
0288: return _eventType = COMMENT;
0289: case STATE_COMMENT_AS_STRING:
0290: _charSequence = readContentString();
0291:
0292: return _eventType = COMMENT;
0293: case STATE_PROCESSING_INSTRUCTION:
0294: _piTarget = readStructureString();
0295: _piData = readStructureString();
0296:
0297: return _eventType = PROCESSING_INSTRUCTION;
0298: case STATE_END:
0299: if (_depth > 1) {
0300: // normal case
0301: return _eventType = END_ELEMENT;
0302: } else if (_depth == 1) {
0303: // this is the last end element for the current tree.
0304: if (_fragmentMode) {
0305: if (--_treeCount == 0) // is this the last tree in the forest?
0306: _completionState = PENDING_END_DOCUMENT;
0307: }
0308: return _eventType = END_ELEMENT;
0309: } else {
0310: // this only happens when we are processing a full document
0311: // and we hit the "end of document" marker
0312: _namespaceAIIsEnd = 0;
0313: _completionState = COMPLETED;
0314: return _eventType = END_DOCUMENT;
0315: }
0316: default:
0317: throw new XMLStreamException("Invalid State");
0318: }
0319: // this should be unreachable
0320: }
0321: }
0322:
0323: public final void require(int type, String namespaceURI,
0324: String localName) throws XMLStreamException {
0325: if (type != _eventType) {
0326: throw new XMLStreamException("");
0327: }
0328: if (namespaceURI != null
0329: && !namespaceURI.equals(getNamespaceURI())) {
0330: throw new XMLStreamException("");
0331: }
0332: if (localName != null && !localName.equals(getLocalName())) {
0333: throw new XMLStreamException("");
0334: }
0335: }
0336:
0337: public final String getElementTextTrim() throws XMLStreamException {
0338: // TODO getElementText* methods more efficiently
0339: return getElementText().trim();
0340: }
0341:
0342: public final String getElementText() throws XMLStreamException {
0343: if (_eventType != START_ELEMENT) {
0344: throw new XMLStreamException("");
0345: }
0346:
0347: next();
0348: return getElementText(true);
0349: }
0350:
0351: public final String getElementText(boolean startElementRead)
0352: throws XMLStreamException {
0353: if (!startElementRead) {
0354: throw new XMLStreamException("");
0355: }
0356:
0357: int eventType = getEventType();
0358: StringBuffer content = new StringBuffer();
0359: while (eventType != END_ELEMENT) {
0360: if (eventType == CHARACTERS || eventType == CDATA
0361: || eventType == SPACE
0362: || eventType == ENTITY_REFERENCE) {
0363: content.append(getText());
0364: } else if (eventType == PROCESSING_INSTRUCTION
0365: || eventType == COMMENT) {
0366: // skipping
0367: } else if (eventType == END_DOCUMENT) {
0368: throw new XMLStreamException("");
0369: } else if (eventType == START_ELEMENT) {
0370: throw new XMLStreamException("");
0371: } else {
0372: throw new XMLStreamException("");
0373: }
0374: eventType = next();
0375: }
0376: return content.toString();
0377: }
0378:
0379: public final int nextTag() throws XMLStreamException {
0380: next();
0381: return nextTag(true);
0382: }
0383:
0384: public final int nextTag(boolean currentTagRead)
0385: throws XMLStreamException {
0386: int eventType = getEventType();
0387: if (!currentTagRead) {
0388: eventType = next();
0389: }
0390: while ((eventType == CHARACTERS && isWhiteSpace()) // skip whitespace
0391: || (eventType == CDATA && isWhiteSpace())
0392: || eventType == SPACE
0393: || eventType == PROCESSING_INSTRUCTION
0394: || eventType == COMMENT) {
0395: eventType = next();
0396: }
0397: if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
0398: throw new XMLStreamException("");
0399: }
0400: return eventType;
0401: }
0402:
0403: public final boolean hasNext() {
0404: return (_eventType != END_DOCUMENT);
0405: }
0406:
0407: public void close() throws XMLStreamException {
0408: }
0409:
0410: public final boolean isStartElement() {
0411: return (_eventType == START_ELEMENT);
0412: }
0413:
0414: public final boolean isEndElement() {
0415: return (_eventType == END_ELEMENT);
0416: }
0417:
0418: public final boolean isCharacters() {
0419: return (_eventType == CHARACTERS);
0420: }
0421:
0422: public final boolean isWhiteSpace() {
0423: if (isCharacters() || (_eventType == CDATA)) {
0424: char[] ch = this .getTextCharacters();
0425: int start = this .getTextStart();
0426: int length = this .getTextLength();
0427: for (int i = start; i < length; i++) {
0428: final char c = ch[i];
0429: if (!(c == 0x20 || c == 0x9 || c == 0xD || c == 0xA))
0430: return false;
0431: }
0432: return true;
0433: }
0434: return false;
0435: }
0436:
0437: public final String getAttributeValue(String namespaceURI,
0438: String localName) {
0439: if (_eventType != START_ELEMENT) {
0440: throw new IllegalStateException("");
0441: }
0442:
0443: if (namespaceURI == null) {
0444: // Set to the empty string to be compatible with the
0445: // org.xml.sax.Attributes interface
0446: namespaceURI = "";
0447: }
0448:
0449: return _attributeCache.getValue(namespaceURI, localName);
0450: }
0451:
0452: public final int getAttributeCount() {
0453: if (_eventType != START_ELEMENT) {
0454: throw new IllegalStateException("");
0455: }
0456:
0457: return _attributeCache.getLength();
0458: }
0459:
0460: public final javax.xml.namespace.QName getAttributeName(int index) {
0461: if (_eventType != START_ELEMENT) {
0462: throw new IllegalStateException("");
0463: }
0464:
0465: final String prefix = _attributeCache.getPrefix(index);
0466: final String localName = _attributeCache.getLocalName(index);
0467: final String uri = _attributeCache.getURI(index);
0468: return new QName(uri, localName, prefix);
0469: }
0470:
0471: public final String getAttributeNamespace(int index) {
0472: if (_eventType != START_ELEMENT) {
0473: throw new IllegalStateException("");
0474: }
0475: return fixEmptyString(_attributeCache.getURI(index));
0476: }
0477:
0478: public final String getAttributeLocalName(int index) {
0479: if (_eventType != START_ELEMENT) {
0480: throw new IllegalStateException("");
0481: }
0482: return _attributeCache.getLocalName(index);
0483: }
0484:
0485: public final String getAttributePrefix(int index) {
0486: if (_eventType != START_ELEMENT) {
0487: throw new IllegalStateException("");
0488: }
0489: return fixEmptyString(_attributeCache.getPrefix(index));
0490: }
0491:
0492: public final String getAttributeType(int index) {
0493: if (_eventType != START_ELEMENT) {
0494: throw new IllegalStateException("");
0495: }
0496: return _attributeCache.getType(index);
0497: }
0498:
0499: public final String getAttributeValue(int index) {
0500: if (_eventType != START_ELEMENT) {
0501: throw new IllegalStateException("");
0502: }
0503:
0504: return _attributeCache.getValue(index);
0505: }
0506:
0507: public final boolean isAttributeSpecified(int index) {
0508: return false;
0509: }
0510:
0511: public final int getNamespaceCount() {
0512: if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
0513: return _stackTop.namespaceAIIsEnd
0514: - _stackTop.namespaceAIIsStart;
0515: }
0516:
0517: throw new IllegalStateException("");
0518: }
0519:
0520: public final String getNamespacePrefix(int index) {
0521: if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
0522: return _namespaceAIIsPrefix[_stackTop.namespaceAIIsStart
0523: + index];
0524: }
0525:
0526: throw new IllegalStateException("");
0527: }
0528:
0529: public final String getNamespaceURI(int index) {
0530: if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) {
0531: return _namespaceAIIsNamespaceName[_stackTop.namespaceAIIsStart
0532: + index];
0533: }
0534:
0535: throw new IllegalStateException("");
0536: }
0537:
0538: public final String getNamespaceURI(String prefix) {
0539: return _nsCtx.getNamespaceURI(prefix);
0540: }
0541:
0542: public final NamespaceContextEx getNamespaceContext() {
0543: return _nsCtx;
0544: }
0545:
0546: public final int getEventType() {
0547: return _eventType;
0548: }
0549:
0550: public final String getText() {
0551: if (_characters != null) {
0552: String s = new String(_characters, _textOffset, _textLen);
0553: _charSequence = s;
0554: return s;
0555: } else if (_charSequence != null) {
0556: return _charSequence.toString();
0557: } else {
0558: throw new IllegalStateException();
0559: }
0560: }
0561:
0562: public final char[] getTextCharacters() {
0563: if (_characters != null) {
0564: return _characters;
0565: } else if (_charSequence != null) {
0566: // TODO try to avoid creation of a temporary String for some
0567: // CharSequence implementations
0568: _characters = _charSequence.toString().toCharArray();
0569: _textLen = _characters.length;
0570: _textOffset = 0;
0571: return _characters;
0572: } else {
0573: throw new IllegalStateException();
0574: }
0575: }
0576:
0577: public final int getTextStart() {
0578: if (_characters != null) {
0579: return _textOffset;
0580: } else if (_charSequence != null) {
0581: return 0;
0582: } else {
0583: throw new IllegalStateException();
0584: }
0585: }
0586:
0587: public final int getTextLength() {
0588: if (_characters != null) {
0589: return _textLen;
0590: } else if (_charSequence != null) {
0591: return _charSequence.length();
0592: } else {
0593: throw new IllegalStateException();
0594: }
0595: }
0596:
0597: public final int getTextCharacters(int sourceStart, char[] target,
0598: int targetStart, int length) throws XMLStreamException {
0599: if (_characters != null) {
0600: } else if (_charSequence != null) {
0601: _characters = _charSequence.toString().toCharArray();
0602: _textLen = _characters.length;
0603: _textOffset = 0;
0604: } else {
0605: throw new IllegalStateException("");
0606: }
0607:
0608: try {
0609: System.arraycopy(_characters, sourceStart, target,
0610: targetStart, length);
0611: return length;
0612: } catch (IndexOutOfBoundsException e) {
0613: throw new XMLStreamException(e);
0614: }
0615: }
0616:
0617: private class CharSequenceImpl implements CharSequence {
0618: private final int _offset;
0619: private final int _length;
0620:
0621: CharSequenceImpl(int offset, int length) {
0622: _offset = offset;
0623: _length = length;
0624: }
0625:
0626: public int length() {
0627: return _length;
0628: }
0629:
0630: public char charAt(int index) {
0631: if (index >= 0 && index < _textLen) {
0632: return _characters[_textOffset + index];
0633: } else {
0634: throw new IndexOutOfBoundsException();
0635: }
0636: }
0637:
0638: public CharSequence subSequence(int start, int end) {
0639: final int length = end - start;
0640: if (end < 0 || start < 0 || end > length || start > end) {
0641: throw new IndexOutOfBoundsException();
0642: }
0643:
0644: return new CharSequenceImpl(_offset + start, length);
0645: }
0646:
0647: public String toString() {
0648: return new String(_characters, _offset, _length);
0649: }
0650: }
0651:
0652: public final CharSequence getPCDATA() {
0653: if (_characters != null) {
0654: return new CharSequenceImpl(_textOffset, _textLen);
0655: } else if (_charSequence != null) {
0656: return _charSequence;
0657: } else {
0658: throw new IllegalStateException();
0659: }
0660: }
0661:
0662: public final String getEncoding() {
0663: return "UTF-8";
0664: }
0665:
0666: public final boolean hasText() {
0667: return (_characters != null || _charSequence != null);
0668: }
0669:
0670: public final Location getLocation() {
0671: return new DummyLocation();
0672: }
0673:
0674: public final boolean hasName() {
0675: return (_eventType == START_ELEMENT || _eventType == END_ELEMENT);
0676: }
0677:
0678: public final QName getName() {
0679: return _stackTop.getQName();
0680: }
0681:
0682: public final String getLocalName() {
0683: return _stackTop.localName;
0684: }
0685:
0686: public final String getNamespaceURI() {
0687: return _stackTop.uri;
0688: }
0689:
0690: public final String getPrefix() {
0691: return _stackTop.prefix;
0692:
0693: }
0694:
0695: public final String getVersion() {
0696: return "1.0";
0697: }
0698:
0699: public final boolean isStandalone() {
0700: return false;
0701: }
0702:
0703: public final boolean standaloneSet() {
0704: return false;
0705: }
0706:
0707: public final String getCharacterEncodingScheme() {
0708: return "UTF-8";
0709: }
0710:
0711: public final String getPITarget() {
0712: if (_eventType == PROCESSING_INSTRUCTION) {
0713: return _piTarget;
0714: }
0715: throw new IllegalStateException("");
0716: }
0717:
0718: public final String getPIData() {
0719: if (_eventType == PROCESSING_INSTRUCTION) {
0720: return _piData;
0721: }
0722: throw new IllegalStateException("");
0723: }
0724:
0725: protected void processElement(String prefix, String uri,
0726: String localName) {
0727: pushElementStack();
0728: _stackTop.set(prefix, uri, localName);
0729:
0730: _attributeCache.clear();
0731:
0732: int item = peekStructure();
0733: if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
0734: // Skip the namespace declarations on the element
0735: // they will have been added already
0736: item = processNamespaceAttributes(item);
0737: }
0738: if ((item & TYPE_MASK) == T_ATTRIBUTE) {
0739: processAttributes(item);
0740: }
0741: }
0742:
0743: private void resizeNamespaceAttributes() {
0744: final String[] namespaceAIIsPrefix = new String[_namespaceAIIsEnd * 2];
0745: System.arraycopy(_namespaceAIIsPrefix, 0, namespaceAIIsPrefix,
0746: 0, _namespaceAIIsEnd);
0747: _namespaceAIIsPrefix = namespaceAIIsPrefix;
0748:
0749: final String[] namespaceAIIsNamespaceName = new String[_namespaceAIIsEnd * 2];
0750: System.arraycopy(_namespaceAIIsNamespaceName, 0,
0751: namespaceAIIsNamespaceName, 0, _namespaceAIIsEnd);
0752: _namespaceAIIsNamespaceName = namespaceAIIsNamespaceName;
0753: }
0754:
0755: private int processNamespaceAttributes(int item) {
0756: _stackTop.namespaceAIIsStart = _namespaceAIIsEnd;
0757:
0758: do {
0759: if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) {
0760: resizeNamespaceAttributes();
0761: }
0762:
0763: switch (_niiStateTable[item]) {
0764: case STATE_NAMESPACE_ATTRIBUTE:
0765: // Undeclaration of default namespace
0766: _namespaceAIIsPrefix[_namespaceAIIsEnd] = _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = "";
0767: break;
0768: case STATE_NAMESPACE_ATTRIBUTE_P:
0769: // Undeclaration of namespace
0770: _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString();
0771: _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = "";
0772: break;
0773: case STATE_NAMESPACE_ATTRIBUTE_P_U:
0774: // Declaration with prefix
0775: _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString();
0776: _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString();
0777: break;
0778: case STATE_NAMESPACE_ATTRIBUTE_U:
0779: // Default declaration
0780: _namespaceAIIsPrefix[_namespaceAIIsEnd] = "";
0781: _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString();
0782: break;
0783: }
0784: readStructure();
0785:
0786: item = peekStructure();
0787: } while ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
0788:
0789: _stackTop.namespaceAIIsEnd = _namespaceAIIsEnd;
0790:
0791: return item;
0792: }
0793:
0794: private void processAttributes(int item) {
0795: do {
0796: switch (_aiiStateTable[item]) {
0797: case STATE_ATTRIBUTE_U_LN_QN: {
0798: final String uri = readStructureString();
0799: final String localName = readStructureString();
0800: final String prefix = getPrefixFromQName(readStructureString());
0801: _attributeCache.addAttributeWithPrefix(prefix, uri,
0802: localName, readStructureString(),
0803: readContentString());
0804: break;
0805: }
0806: case STATE_ATTRIBUTE_P_U_LN:
0807: _attributeCache.addAttributeWithPrefix(
0808: readStructureString(), readStructureString(),
0809: readStructureString(), readStructureString(),
0810: readContentString());
0811: break;
0812: case STATE_ATTRIBUTE_U_LN:
0813: // _attributeCache follows SAX convention
0814: _attributeCache.addAttributeWithPrefix("",
0815: readStructureString(), readStructureString(),
0816: readStructureString(), readContentString());
0817: break;
0818: case STATE_ATTRIBUTE_LN: {
0819: _attributeCache.addAttributeWithPrefix("", "",
0820: readStructureString(), readStructureString(),
0821: readContentString());
0822: break;
0823: }
0824: }
0825: readStructure();
0826:
0827: item = peekStructure();
0828: } while ((item & TYPE_MASK) == T_ATTRIBUTE);
0829: }
0830:
0831: private void pushElementStack() {
0832: if (_depth == _stack.length) {
0833: // resize stack
0834: ElementStackEntry[] tmp = _stack;
0835: _stack = new ElementStackEntry[_stack.length * 3 / 2 + 1];
0836: System.arraycopy(tmp, 0, _stack, 0, tmp.length);
0837: for (int i = tmp.length; i < _stack.length; i++) {
0838: _stack[i] = new ElementStackEntry();
0839: }
0840: }
0841:
0842: _stackTop = _stack[_depth++];
0843: }
0844:
0845: private void popElementStack(int depth) {
0846: // _depth is checked outside this method
0847: _stackTop = _stack[depth - 1];
0848: // Move back the position of the namespace index
0849: _namespaceAIIsEnd = _stack[depth].namespaceAIIsStart;
0850: }
0851:
0852: private final class ElementStackEntry {
0853: /**
0854: * Prefix.
0855: * Just like everywhere else in StAX, this can be null but can't be empty.
0856: */
0857: String prefix;
0858: /**
0859: * Namespace URI.
0860: * Just like everywhere else in StAX, this can be null but can't be empty.
0861: */
0862: String uri;
0863: String localName;
0864: QName qname;
0865:
0866: // Start and end of namespace declarations
0867: // in namespace declaration arrays
0868: int namespaceAIIsStart;
0869: int namespaceAIIsEnd;
0870:
0871: public void set(String prefix, String uri, String localName) {
0872: this .prefix = prefix;
0873: this .uri = uri;
0874: this .localName = localName;
0875: this .qname = null;
0876:
0877: this .namespaceAIIsStart = this .namespaceAIIsEnd = StreamReaderBufferProcessor.this ._namespaceAIIsEnd;
0878: }
0879:
0880: public QName getQName() {
0881: if (qname == null) {
0882: qname = new QName(fixNull(uri), localName,
0883: fixNull(prefix));
0884: }
0885: return qname;
0886: }
0887:
0888: private String fixNull(String s) {
0889: return (s == null) ? "" : s;
0890: }
0891: }
0892:
0893: private final class InternalNamespaceContext implements
0894: NamespaceContextEx {
0895: @SuppressWarnings({"StringEquality"})
0896: public String getNamespaceURI(String prefix) {
0897: if (prefix == null) {
0898: throw new IllegalArgumentException(
0899: "Prefix cannot be null");
0900: }
0901:
0902: /*
0903: * If the buffer was created using string interning
0904: * intern the prefix and check for reference equality
0905: * rather than using String.equals();
0906: */
0907: if (_stringInterningFeature) {
0908: prefix = prefix.intern();
0909:
0910: // Find the most recently declared prefix
0911: for (int i = _namespaceAIIsEnd - 1; i >= 0; i--) {
0912: if (prefix == _namespaceAIIsPrefix[i]) {
0913: return _namespaceAIIsNamespaceName[i];
0914: }
0915: }
0916: } else {
0917: // Find the most recently declared prefix
0918: for (int i = _namespaceAIIsEnd - 1; i >= 0; i--) {
0919: if (prefix.equals(_namespaceAIIsPrefix[i])) {
0920: return _namespaceAIIsNamespaceName[i];
0921: }
0922: }
0923: }
0924:
0925: // Check for XML-based prefixes
0926: if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
0927: return XMLConstants.XML_NS_URI;
0928: } else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
0929: return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
0930: }
0931:
0932: return null;
0933: }
0934:
0935: public String getPrefix(String namespaceURI) {
0936: final Iterator i = getPrefixes(namespaceURI);
0937: if (i.hasNext()) {
0938: return (String) i.next();
0939: } else {
0940: return null;
0941: }
0942: }
0943:
0944: public Iterator getPrefixes(final String namespaceURI) {
0945: if (namespaceURI == null) {
0946: throw new IllegalArgumentException(
0947: "NamespaceURI cannot be null");
0948: }
0949:
0950: if (namespaceURI.equals(XMLConstants.XML_NS_URI)) {
0951: return Collections.singletonList(
0952: XMLConstants.XML_NS_PREFIX).iterator();
0953: } else if (namespaceURI
0954: .equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
0955: return Collections.singletonList(
0956: XMLConstants.XMLNS_ATTRIBUTE).iterator();
0957: }
0958:
0959: return new Iterator() {
0960: private int i = _namespaceAIIsEnd - 1;
0961: private boolean requireFindNext = true;
0962: private String p;
0963:
0964: private String findNext() {
0965: while (i >= 0) {
0966: // Find the most recently declared namespace
0967: if (namespaceURI
0968: .equals(_namespaceAIIsNamespaceName[i])) {
0969: // Find the most recently declared prefix of the namespace
0970: // and check if the prefix is in scope with that namespace
0971: if (getNamespaceURI(_namespaceAIIsPrefix[i])
0972: .equals(
0973: _namespaceAIIsNamespaceName[i])) {
0974: return p = _namespaceAIIsPrefix[i];
0975: }
0976: }
0977: i--;
0978: }
0979: return p = null;
0980: }
0981:
0982: public boolean hasNext() {
0983: if (requireFindNext) {
0984: findNext();
0985: requireFindNext = false;
0986: }
0987: return (p != null);
0988: }
0989:
0990: public Object next() {
0991: if (requireFindNext) {
0992: findNext();
0993: }
0994: requireFindNext = true;
0995:
0996: if (p == null) {
0997: throw new NoSuchElementException();
0998: }
0999:
1000: return p;
1001: }
1002:
1003: public void remove() {
1004: throw new UnsupportedOperationException();
1005: }
1006: };
1007: }
1008:
1009: private class BindingImpl implements NamespaceContextEx.Binding {
1010: final String _prefix;
1011: final String _namespaceURI;
1012:
1013: BindingImpl(String prefix, String namespaceURI) {
1014: _prefix = prefix;
1015: _namespaceURI = namespaceURI;
1016: }
1017:
1018: public String getPrefix() {
1019: return _prefix;
1020: }
1021:
1022: public String getNamespaceURI() {
1023: return _namespaceURI;
1024: }
1025: }
1026:
1027: public Iterator<NamespaceContextEx.Binding> iterator() {
1028: return new Iterator<NamespaceContextEx.Binding>() {
1029: private final int end = _namespaceAIIsEnd - 1;
1030: private int current = end;
1031: private boolean requireFindNext = true;
1032: private NamespaceContextEx.Binding namespace;
1033:
1034: private NamespaceContextEx.Binding findNext() {
1035: while (current >= 0) {
1036: final String prefix = _namespaceAIIsPrefix[current];
1037:
1038: // Find if the current prefix occurs more recently
1039: // If so then it is not in scope
1040: int i = end;
1041: for (; i > current; i--) {
1042: if (prefix.equals(_namespaceAIIsPrefix[i])) {
1043: break;
1044: }
1045: }
1046: if (i == current--) {
1047: // The current prefix is in-scope
1048: return namespace = new BindingImpl(
1049: prefix,
1050: _namespaceAIIsNamespaceName[current]);
1051: }
1052: }
1053: return namespace = null;
1054: }
1055:
1056: public boolean hasNext() {
1057: if (requireFindNext) {
1058: findNext();
1059: requireFindNext = false;
1060: }
1061: return (namespace != null);
1062: }
1063:
1064: public NamespaceContextEx.Binding next() {
1065: if (requireFindNext) {
1066: findNext();
1067: }
1068: requireFindNext = true;
1069:
1070: if (namespace == null) {
1071: throw new NoSuchElementException();
1072: }
1073:
1074: return namespace;
1075: }
1076:
1077: public void remove() {
1078: throw new UnsupportedOperationException();
1079: }
1080: };
1081: }
1082: }
1083:
1084: private class DummyLocation implements Location {
1085: public int getLineNumber() {
1086: return -1;
1087: }
1088:
1089: public int getColumnNumber() {
1090: return -1;
1091: }
1092:
1093: public int getCharacterOffset() {
1094: return -1;
1095: }
1096:
1097: public String getPublicId() {
1098: return null;
1099: }
1100:
1101: public String getSystemId() {
1102: return _buffer.getSystemId();
1103: }
1104: }
1105:
1106: private static String fixEmptyString(String s) {
1107: // s must not be null, so no need to check for that. that would be bug.
1108: if (s.length() == 0)
1109: return null;
1110: else
1111: return s;
1112: }
1113:
1114: }
|