0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson, Adam Megacz
0028: */
0029:
0030: package com.caucho.xml.stream;
0031:
0032: import com.caucho.util.CharBuffer;
0033: import com.caucho.util.L10N;
0034: import com.caucho.vfs.*;
0035:
0036: import javax.xml.XMLConstants;
0037: import javax.xml.namespace.NamespaceContext;
0038: import javax.xml.namespace.QName;
0039: import javax.xml.stream.Location;
0040: import javax.xml.stream.XMLStreamException;
0041: import javax.xml.stream.XMLStreamReader;
0042: import java.io.IOException;
0043: import java.io.InputStream;
0044: import java.io.Reader;
0045: import java.util.logging.Logger;
0046:
0047: /**
0048: * XML pull-parser interface.
0049: */
0050: public class XMLStreamReaderImpl implements XMLStreamReader {
0051: private static final Logger log = Logger
0052: .getLogger(XMLStreamReaderImpl.class.getName());
0053: private static final L10N L = new L10N(XMLStreamReaderImpl.class);
0054:
0055: private static final boolean[] IS_XML_NAME = new boolean[65536];
0056:
0057: private StaxIntern _intern;
0058:
0059: private ReadStream _is;
0060: private Reader _reader;
0061:
0062: private int _lastCol = 1;
0063: private int _col = 1;
0064: private int _row = 1;
0065: private int _ofs = 1;
0066:
0067: private NamespaceReaderContext _namespaceTracker;
0068:
0069: private String _version = "1.0";
0070: private String _encoding = "utf-8";
0071: private String _encodingScheme;
0072:
0073: private String _publicId;
0074: private String _systemId;
0075:
0076: private boolean _seenDocumentStart = false;
0077: private int _current;
0078: private int _state;
0079: private boolean _isShortTag;
0080: private boolean _isWhitespace = false;
0081:
0082: private boolean _eofEncountered = false;
0083:
0084: private String _processingInstructionTarget;
0085: private String _processingInstructionData;
0086:
0087: private RawName _rawTagName = new RawName();
0088: private QName _name;
0089:
0090: private StaxIntern.Entry[] _attrRawNames = new StaxIntern.Entry[16];
0091: private QName[] _attrNames = new QName[16];
0092: private String[] _attrValues = new String[16];
0093: private int _attrCount;
0094: private final StringBuilder _sb = new StringBuilder();
0095:
0096: private TempCharBuffer _tempInputBuffer;
0097: private char[] _inputBuf;
0098: private int _inputOffset;
0099: private int _inputLength;
0100:
0101: private TempCharBuffer _tempCharBuffer;
0102: private char[] _cBuf;
0103: private int _cBufLength;
0104:
0105: public XMLStreamReaderImpl(InputStream is)
0106: throws XMLStreamException {
0107: this (Vfs.openRead(is));
0108: }
0109:
0110: public XMLStreamReaderImpl(Reader r) throws XMLStreamException {
0111: _reader = r;
0112: init();
0113: }
0114:
0115: public XMLStreamReaderImpl(InputStream is, String systemId)
0116: throws XMLStreamException {
0117: this (Vfs.openRead(is));
0118: _systemId = systemId;
0119: }
0120:
0121: public XMLStreamReaderImpl(Reader r, String systemId)
0122: throws XMLStreamException {
0123: this (r);
0124: _systemId = systemId;
0125: }
0126:
0127: public XMLStreamReaderImpl(ReadStream is) throws XMLStreamException {
0128: _is = is;
0129: init();
0130: }
0131:
0132: public void init() throws XMLStreamException {
0133: _namespaceTracker = new NamespaceReaderContext();
0134: _intern = new StaxIntern(_namespaceTracker);
0135:
0136: _tempCharBuffer = TempCharBuffer.allocate();
0137: _cBuf = _tempCharBuffer.getBuffer();
0138:
0139: _tempInputBuffer = TempCharBuffer.allocate();
0140: _inputBuf = _tempInputBuffer.getBuffer();
0141: _inputOffset = _inputLength = 0;
0142:
0143: readHeader();
0144:
0145: _current = START_DOCUMENT;
0146: }
0147:
0148: public int getAttributeCount() {
0149: return _attrCount;
0150: }
0151:
0152: public String getAttributeLocalName(int index) {
0153: if (_attrCount <= index)
0154: throw new IllegalArgumentException(L.l(
0155: "element only has {0} attributes, given index {1}",
0156: _attrCount, index));
0157:
0158: return _attrNames[index].getLocalPart();
0159: }
0160:
0161: public QName getAttributeName(int index) {
0162: if (_attrCount <= index)
0163: throw new IllegalArgumentException(L.l(
0164: "element only has {0} attributes, given index {1}",
0165: _attrCount, index));
0166:
0167: return _attrNames[index];
0168: }
0169:
0170: public String getAttributeNamespace(int index) {
0171: if (_attrCount <= index)
0172: throw new IllegalArgumentException(L.l(
0173: "element only has {0} attributes, given index {1}",
0174: _attrCount, index));
0175:
0176: String ret = _attrNames[index].getNamespaceURI();
0177:
0178: // API quirk
0179: if ("".equals(ret))
0180: return null;
0181:
0182: return ret;
0183: }
0184:
0185: public String getAttributePrefix(int index) {
0186: if (_attrCount <= index)
0187: throw new IllegalArgumentException(L.l(
0188: "element only has {0} attributes, given index {1}",
0189: _attrCount, index));
0190:
0191: String ret = _attrNames[index].getPrefix();
0192:
0193: return ret;
0194: }
0195:
0196: public String getAttributeType(int index) {
0197: return "CDATA";
0198: }
0199:
0200: public String getAttributeValue(int index) {
0201: if (_attrCount <= index)
0202: throw new IllegalArgumentException(L.l(
0203: "element only has {0} attributes, given index {1}",
0204: _attrCount, index));
0205:
0206: return _attrValues[index];
0207: }
0208:
0209: public boolean isAttributeSpecified(int index) {
0210: return index < _attrCount;
0211: }
0212:
0213: public String getAttributeValue(String namespaceURI,
0214: String localName) {
0215: for (int i = _attrCount - 1; i >= 0; i--) {
0216: QName name = _attrNames[i];
0217:
0218: // namespaceURI == null means ignore namespace
0219: if (namespaceURI == null) {
0220: if (name.getLocalPart().equals(localName))
0221: return _attrValues[i];
0222: } else if (name.getLocalPart().equals(localName)
0223: && name.getNamespaceURI().equals(namespaceURI))
0224: return _attrValues[i];
0225: }
0226:
0227: return null;
0228: }
0229:
0230: public String getCharacterEncodingScheme() {
0231: return _encodingScheme;
0232: }
0233:
0234: public String getElementText() throws XMLStreamException {
0235: if (_current != START_ELEMENT)
0236: throw new XMLStreamException(
0237: L
0238: .l("START_ELEMENT expected when calling getElementText()"));
0239:
0240: StringBuilder sb = new StringBuilder();
0241:
0242: for (int eventType = next(); eventType != END_ELEMENT; eventType = next()) {
0243: switch (eventType) {
0244: case CHARACTERS:
0245: case CDATA:
0246: case SPACE:
0247: case ENTITY_REFERENCE:
0248: sb.append(_cBuf, 0, _cBufLength);
0249: break;
0250:
0251: case PROCESSING_INSTRUCTION:
0252: case COMMENT:
0253: break;
0254:
0255: case END_DOCUMENT:
0256: throw new XMLStreamException(
0257: L
0258: .l("Document ended unexpectedly while reading element text"));
0259:
0260: case START_ELEMENT:
0261: throw new XMLStreamException(
0262: L
0263: .l("getElementText() encountered a START_ELEMENT; text only element expected"));
0264:
0265: default:
0266: throw new XMLStreamException(
0267: L
0268: .l(
0269: "Unexpected event during getElementText(): {0}",
0270: eventType));
0271: }
0272: }
0273:
0274: return sb.toString();
0275: }
0276:
0277: public String getEncoding() {
0278: return _encoding;
0279: }
0280:
0281: public int getEventType() {
0282: return _current;
0283: }
0284:
0285: public Location getLocation() {
0286: return new StreamReaderLocation(_ofs, _row, _col);
0287: }
0288:
0289: public String getLocalName() {
0290: if (_name == null)
0291: throw new IllegalStateException();
0292:
0293: return _name.getLocalPart();
0294: }
0295:
0296: public String getNamespaceURI() {
0297: if (_name == null)
0298: return null;
0299:
0300: String uri = _name.getNamespaceURI();
0301:
0302: if ("".equals(uri))
0303: return null;
0304: else
0305: return uri;
0306: }
0307:
0308: public QName getName() {
0309: return _name;
0310: }
0311:
0312: public NamespaceContext getNamespaceContext() {
0313: return _namespaceTracker;
0314: }
0315:
0316: public int getNamespaceCount() {
0317: return _namespaceTracker.getNumDecls();
0318: }
0319:
0320: public String getNamespacePrefix(int index) {
0321: String prefix = _namespaceTracker.getPrefix(index);
0322:
0323: // The API specifies that this function return a different value for
0324: // the default namespace, null, than any other function, which all return
0325: // the constant defined in XMLConstants.
0326: if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))
0327: return null;
0328: else
0329: return prefix;
0330: }
0331:
0332: public String getNamespaceURI(int index) {
0333: return _namespaceTracker.getUri(index);
0334: }
0335:
0336: public String getNamespaceURI(String prefix) {
0337: return _namespaceTracker.getUri(prefix);
0338: }
0339:
0340: public String getPIData() {
0341: if (_current != PROCESSING_INSTRUCTION)
0342: return null;
0343:
0344: return _processingInstructionData;
0345: }
0346:
0347: public String getPITarget() {
0348: if (_current != PROCESSING_INSTRUCTION)
0349: return null;
0350:
0351: return _processingInstructionTarget;
0352: }
0353:
0354: public String getPrefix() {
0355: if (_name == null)
0356: return null;
0357:
0358: String prefix = _name.getPrefix();
0359:
0360: // xml/3000, xml/3009
0361: if ("" == prefix && "" == _name.getNamespaceURI())
0362: return null;
0363:
0364: return prefix;
0365: }
0366:
0367: public Object getProperty(String name)
0368: throws IllegalArgumentException {
0369: if ("javax.xml.stream.notations".equals(name)) {
0370: throw new UnsupportedOperationException(getClass()
0371: .getName());
0372: } else if ("javax.xml.stream.entities".equals(name)) {
0373: throw new UnsupportedOperationException(getClass()
0374: .getName());
0375: } else {
0376: throw new IllegalArgumentException("property \"" + name
0377: + "+\" not supported");
0378: }
0379: }
0380:
0381: /**
0382: * Returns the current text string.
0383: */
0384: public String getText() {
0385: return new String(_cBuf, 0, _cBufLength);
0386: }
0387:
0388: /**
0389: * Returns a character buffer for the current text.
0390: */
0391: public char[] getTextCharacters() {
0392: return _cBuf;
0393: }
0394:
0395: /**
0396: * Reads the current text into a buffer.
0397: */
0398: public int getTextCharacters(int sourceStart, char[] target,
0399: int targetStart, int length) throws XMLStreamException {
0400: int sublen = _cBufLength - sourceStart;
0401:
0402: if (length < sublen)
0403: sublen = length;
0404:
0405: System.arraycopy(_cBuf, sourceStart, target, targetStart,
0406: sublen);
0407:
0408: return sublen;
0409: }
0410:
0411: /**
0412: * Returns the length of the current text.
0413: */
0414: public int getTextLength() {
0415: return _cBufLength;
0416: }
0417:
0418: /**
0419: * Returns the offset of the current text.
0420: */
0421: public int getTextStart() {
0422: return 0;
0423: }
0424:
0425: public String getVersion() {
0426: return _version;
0427: }
0428:
0429: public boolean hasName() {
0430: return _name != null;
0431: }
0432:
0433: public boolean hasText() {
0434: switch (getEventType()) {
0435: case CHARACTERS:
0436: case DTD:
0437: case ENTITY_REFERENCE:
0438: case COMMENT:
0439: case SPACE:
0440: return true;
0441: default:
0442: return false;
0443: }
0444: }
0445:
0446: public boolean isCharacters() {
0447: return _current == CHARACTERS;
0448: }
0449:
0450: public boolean isEndElement() {
0451: if (_current == END_ELEMENT)
0452: return true;
0453:
0454: // php/4618
0455: if (_current == START_ELEMENT && _isShortTag)
0456: return true;
0457:
0458: return false;
0459: }
0460:
0461: public boolean isStandalone() {
0462: return false;
0463: }
0464:
0465: public boolean isStartElement() {
0466: return _current == START_ELEMENT;
0467: }
0468:
0469: public boolean isWhiteSpace() {
0470: return (_isWhitespace && (_current == CHARACTERS || _current == SPACE));
0471: }
0472:
0473: /**
0474: * Skips until the next START_ELEMENT or END_ELEMENT
0475: */
0476: public int nextTag() throws XMLStreamException {
0477: while (true) {
0478: int tag = next();
0479:
0480: if (tag < 0 || tag == START_ELEMENT || tag == END_ELEMENT) {
0481: return tag;
0482: }
0483: }
0484: }
0485:
0486: public void require(int type, String namespaceURI, String localName)
0487: throws XMLStreamException {
0488: if (type != _current) {
0489: StringBuilder sb = new StringBuilder();
0490: sb.append("expected ");
0491: sb.append(StaxUtil.constantToString(type));
0492:
0493: if (type == START_ELEMENT || type == END_ELEMENT) {
0494: sb.append('(');
0495: sb.append(localName);
0496: sb.append(')');
0497: }
0498:
0499: sb.append(", found ");
0500: sb.append(StaxUtil.constantToString(_current));
0501:
0502: if (_current == START_ELEMENT || _current == END_ELEMENT) {
0503: sb.append('(');
0504: sb.append(getLocalName());
0505: sb.append(')');
0506: }
0507:
0508: sb.append(" at ");
0509: sb.append(getLocation());
0510:
0511: throw new XMLStreamException(sb.toString());
0512: }
0513:
0514: if (localName != null && !localName.equals(getLocalName())) {
0515: if (type == START_ELEMENT) {
0516: throw new XMLStreamException("expected <" + localName
0517: + ">, found " + "<" + getLocalName() + "> at "
0518: + getLocation());
0519: } else if (type == END_ELEMENT) {
0520: throw new XMLStreamException("expected </" + localName
0521: + ">, found " + "</" + getLocalName() + "> at "
0522: + getLocation());
0523: }
0524: }
0525:
0526: if (namespaceURI != null
0527: && !namespaceURI.equals(getNamespaceURI()))
0528: throw new XMLStreamException("expected xmlns="
0529: + namespaceURI + ", found xmlns="
0530: + getNamespaceURI() + " at " + getLocation());
0531: }
0532:
0533: public boolean standaloneSet() {
0534: return isStandalone();
0535: }
0536:
0537: public boolean hasNext() throws XMLStreamException {
0538: if (_is == null && _reader == null)
0539: return false;
0540:
0541: return _current != END_DOCUMENT;
0542: }
0543:
0544: public int next() throws XMLStreamException {
0545: try {
0546: _current = readNext();
0547: } catch (IOException e) {
0548: throw new XMLStreamException(e);
0549: }
0550:
0551: if (_current > 0)
0552: return _current;
0553: else {
0554: if (_eofEncountered)
0555: return _current = -1;
0556:
0557: _eofEncountered = true;
0558:
0559: return _current = END_DOCUMENT;
0560: }
0561: }
0562:
0563: private int readNext() throws IOException, XMLStreamException {
0564: _cBufLength = 0;
0565:
0566: // we pop the namespace context when the user is finished
0567: // working with the END_ELEMENT event
0568: if (_current == END_ELEMENT)
0569: _namespaceTracker.pop();
0570:
0571: if (_isShortTag) {
0572: _isShortTag = false;
0573: return END_ELEMENT;
0574: }
0575:
0576: _name = null;
0577:
0578: int ch = read();
0579:
0580: if (ch == '<') {
0581: ch = read();
0582:
0583: switch (ch) {
0584: case '/':
0585: _name = readName(false).getQName();
0586:
0587: expect('>');
0588:
0589: return END_ELEMENT;
0590:
0591: case '!':
0592: expect('-');
0593: expect('-');
0594: return readComment();
0595:
0596: case '?':
0597: readProcessingDirective();
0598: return PROCESSING_INSTRUCTION;
0599:
0600: default:
0601: unread();
0602:
0603: readElementBegin();
0604:
0605: return START_ELEMENT;
0606: }
0607: } else if (ch < 0) {
0608: close();
0609:
0610: return -1;
0611: } else {
0612: unread();
0613:
0614: return readData();
0615: }
0616: }
0617:
0618: private void readElementBegin() throws IOException,
0619: XMLStreamException {
0620: _namespaceTracker.push();
0621:
0622: StaxIntern.Entry eltName = readName(false);
0623:
0624: _isShortTag = false;
0625:
0626: int ch = readAttributes();
0627:
0628: if (ch == '>') {
0629: } else if (ch == '/') {
0630: _isShortTag = true;
0631:
0632: expect('>');
0633: } else
0634: throw error(L.l("Expected {0} at {1}", ">", charName(ch)));
0635:
0636: for (int i = _attrCount - 1; i >= 0; i--)
0637: _attrNames[i] = _attrRawNames[i].getQName();
0638:
0639: _name = eltName.getQName();
0640: }
0641:
0642: private int readAttributes() throws IOException, XMLStreamException {
0643: int ch;
0644: int attrCount = 0;
0645:
0646: while ((ch = skipWhitespace()) >= 0 && IS_XML_NAME[ch]) {
0647: unread();
0648:
0649: if (_attrRawNames.length <= attrCount)
0650: extendAttrs();
0651:
0652: StaxIntern.Entry rawName = readName(true);
0653:
0654: ch = skipWhitespace();
0655:
0656: if (ch != '=')
0657: throw error(L.l("attribute expects '=' at {0}",
0658: charName(ch)));
0659:
0660: ch = skipWhitespace();
0661:
0662: if (ch == '\'' || ch == '"') {
0663: if ("xmlns".equals(rawName.getPrefix())) {
0664: _namespaceTracker.declare(rawName.getLocalName(),
0665: readValue(ch));
0666: } else if ("xmlns".equals(rawName.getLocalName())) {
0667: _namespaceTracker.declare(
0668: XMLConstants.DEFAULT_NS_PREFIX,
0669: readValue(ch));
0670: } else {
0671: _attrRawNames[attrCount] = rawName;
0672: _attrValues[attrCount++] = readValue(ch);
0673: }
0674: } else
0675: throw error(L.l("attribute expects value at {0}",
0676: charName(ch)));
0677: }
0678:
0679: _attrCount = attrCount;
0680:
0681: return ch;
0682: }
0683:
0684: private String readValue(int end) throws XMLStreamException {
0685: char[] valueBuffer = _cBuf;
0686: int valueIndex = 0;
0687:
0688: while (true) {
0689: int ch = read();
0690:
0691: switch (ch) {
0692: case -1:
0693: return new String(valueBuffer, 0, valueIndex);
0694:
0695: case '"':
0696: case '\'':
0697: if (ch == end)
0698: return new String(valueBuffer, 0, valueIndex);
0699: else
0700: valueBuffer[valueIndex++] = (char) ch;
0701: break;
0702:
0703: case '&':
0704: valueBuffer[valueIndex++] = (char) ch;
0705: break;
0706:
0707: default:
0708: valueBuffer[valueIndex++] = (char) ch;
0709: break;
0710: }
0711: }
0712: }
0713:
0714: private void extendAttrs() {
0715: int length = _attrRawNames.length;
0716:
0717: StaxIntern.Entry[] attrRawNames = new StaxIntern.Entry[length + 16];
0718: System.arraycopy(_attrRawNames, 0, attrRawNames, 0, length);
0719: _attrRawNames = attrRawNames;
0720:
0721: QName[] attrNames = new QName[length + 16];
0722: System.arraycopy(_attrNames, 0, attrNames, 0, length);
0723: _attrNames = attrNames;
0724:
0725: String[] attrValues = new String[length + 16];
0726: System.arraycopy(_attrValues, 0, attrValues, 0, length);
0727: _attrValues = attrValues;
0728: }
0729:
0730: private int readData() throws IOException, XMLStreamException {
0731: int ch = 0;
0732: _isWhitespace = true;
0733:
0734: int index = 0;
0735: char[] cBuf = _cBuf;
0736: int length = cBuf.length;
0737: int entity = -1;
0738:
0739: loop: for (; index < length && (ch = read()) >= 0; index++) {
0740: switch (ch) {
0741: case '<':
0742: unread();
0743: break loop;
0744:
0745: case '&':
0746: if (cBuf.length <= index + 256) {
0747: unread();
0748: break loop;
0749: }
0750: cBuf[index] = (char) ch;
0751: entity = index;
0752: break;
0753:
0754: case '\r':
0755: ch = read();
0756: if (ch != '\n') {
0757: ch = '\r';
0758: unread();
0759: }
0760:
0761: case ' ':
0762: case '\t':
0763: case '\n':
0764: cBuf[index] = (char) ch;
0765: break;
0766:
0767: case ';':
0768: if (entity >= 0) {
0769: String unresolved = new String(cBuf, entity + 1,
0770: index - entity - 1);
0771: String resolved = resolveEntity(unresolved);
0772: // the loop will advance index + 1
0773: index = entity + resolved.length() - 1;
0774: resolved.getChars(0, resolved.length(), cBuf,
0775: entity);
0776: entity = -1;
0777: break;
0778: }
0779: default:
0780: _isWhitespace = false;
0781: cBuf[index] = (char) ch;
0782: break;
0783: }
0784: }
0785:
0786: if (entity > 0)
0787: throw new XMLStreamException(
0788: "XXX: unclosed entity at end of file");
0789:
0790: _cBufLength = index;
0791:
0792: if (ch < 0 && _isWhitespace)
0793: return -1;
0794:
0795: // whitespace surrounding the root element is "ignorable" per the XML spec
0796: boolean isIgnorableWhitespace = _isWhitespace
0797: && _namespaceTracker.getDepth() == 0;
0798:
0799: return isIgnorableWhitespace ? SPACE : CHARACTERS;
0800: }
0801:
0802: private String resolveEntity(String s) throws XMLStreamException {
0803: if ("amp".equals(s))
0804: return "&";
0805: if ("apos".equals(s))
0806: return "\'";
0807: if ("quot".equals(s))
0808: return "\"";
0809: if ("lt".equals(s))
0810: return "<";
0811: if ("gt".equals(s))
0812: return ">";
0813: if (s.startsWith("#x"))
0814: return "" + ((char) Integer.parseInt(s.substring(2), 16));
0815: if (s.startsWith("#"))
0816: return "" + ((char) Integer.parseInt(s.substring(1)));
0817:
0818: throw new XMLStreamException("unknown entity: \"" + s + "\"");
0819: }
0820:
0821: private void readProcessingDirective() throws XMLStreamException {
0822: CharBuffer target = new CharBuffer();
0823: CharBuffer data = null;
0824:
0825: while (true) {
0826: int ch = read();
0827:
0828: if (ch == -1)
0829: return; /* XXX: error? */
0830:
0831: if (ch == '?') {
0832: int next = read();
0833: if (next == '>') {
0834: _processingInstructionTarget = target.toString();
0835: _processingInstructionData = data == null ? null
0836: : data.toString();
0837: return;
0838: }
0839: unread();
0840: }
0841:
0842: if (data == null && (ch == ' ' || ch == '\r' || ch == '\n')) {
0843: data = new CharBuffer();
0844: continue;
0845: }
0846:
0847: if (data != null)
0848: data.append((char) ch);
0849: else
0850: target.append((char) ch);
0851: }
0852: }
0853:
0854: private int readComment() throws XMLStreamException {
0855: int ch = 0;
0856: int index = 0;
0857: char[] cBuf = _cBuf;
0858: int length = cBuf.length;
0859: loop: for (; index < length && (ch = read()) >= 0; index++) {
0860: cBuf[index] = (char) ch;
0861: if (index > 3 && cBuf[index - 2] == '-'
0862: && cBuf[index - 1] == '-' && cBuf[index - 0] == '>') {
0863: index -= 2;
0864: break;
0865: }
0866: }
0867:
0868: _cBufLength = index;
0869:
0870: return COMMENT;
0871: }
0872:
0873: private void readRawName(RawName name) throws IOException,
0874: XMLStreamException {
0875: int length = 0;
0876: char[] nameBuffer = name._buffer;
0877: int bufferLength = nameBuffer.length;
0878: int prefix = -1;
0879:
0880: int ch;
0881:
0882: while ((ch = read()) >= 0 && IS_XML_NAME[ch]) {
0883: if (bufferLength <= length) {
0884: name.expandCapacity();
0885: nameBuffer = name._buffer;
0886: bufferLength = nameBuffer.length;
0887: }
0888:
0889: if (ch == ':' && prefix < 0)
0890: prefix = length;
0891:
0892: nameBuffer[length++] = (char) ch;
0893: }
0894: unread();
0895:
0896: name._length = length;
0897: name._prefix = prefix;
0898: }
0899:
0900: /**
0901: * Parses a name.
0902: */
0903: private StaxIntern.Entry readName(boolean isAttribute)
0904: throws XMLStreamException {
0905: char[] inputBuf = _inputBuf;
0906: int inputLength = _inputLength;
0907: int inputOffset = _inputOffset;
0908:
0909: char[] valueBuf = _cBuf;
0910: int valueOffset = 0;
0911:
0912: int colon = 0;
0913:
0914: while (true) {
0915: if (inputOffset < inputLength) {
0916: char ch = inputBuf[inputOffset++];
0917:
0918: if (IS_XML_NAME[ch]) {
0919: valueBuf[valueOffset++] = ch;
0920: } else if (ch == ':') {
0921: if (colon <= 0)
0922: colon = valueOffset;
0923:
0924: valueBuf[valueOffset++] = ch;
0925: } else {
0926: _inputOffset = inputOffset - 1;
0927:
0928: return _intern.add(valueBuf, 0, valueOffset, colon,
0929: isAttribute);
0930: }
0931: } else if (fillBuffer()) {
0932: inputLength = _inputLength;
0933: inputOffset = _inputOffset;
0934: } else {
0935: return _intern.add(valueBuf, 0, valueOffset, colon,
0936: isAttribute);
0937: }
0938: }
0939: }
0940:
0941: private static boolean isXmlName(int ch) {
0942: return ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z'
0943: || '0' <= ch && ch <= '9' || ch == ':' || ch == '+'
0944: || ch == '_' || ch == '-');
0945: }
0946:
0947: private int skipWhitespace() throws XMLStreamException {
0948: int ch;
0949:
0950: while ((ch = read()) >= 0
0951: && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')) {
0952: }
0953:
0954: return ch;
0955: }
0956:
0957: /**
0958: * Reads the <?xml ... ?> declaraction
0959: */
0960: private void readHeader() throws XMLStreamException {
0961: // The reading at this point must use the underlying stream because
0962: // the encoding is not determined until the end of the declaration
0963:
0964: int ch;
0965:
0966: ch = readByte();
0967:
0968: if (ch == (char) 0xFE) {
0969: if (readByte() != (char) 0xFF)
0970: throw new XMLStreamException("found unrecognized BOM");
0971:
0972: ch = readByte();
0973: } else if (ch == (char) 0xFF) {
0974: if (readByte() != (char) 0xFE)
0975: throw new UnsupportedOperationException(
0976: "found byte-swapped BOM");
0977: else
0978: throw new XMLStreamException("found unrecognized BOM");
0979: }
0980:
0981: if (ch != '<') {
0982: unreadByte();
0983: } else if ((ch = readByte()) != '?') {
0984: unreadByte();
0985: unreadByte();
0986: } else if ((ch = readByte()) != 'x') {
0987: unreadByte();
0988: unreadByte();
0989: unreadByte();
0990: } else if ((ch = readByte()) != 'm') {
0991: unreadByte();
0992: unreadByte();
0993: unreadByte();
0994: unreadByte();
0995: } else if ((ch = readByte()) != 'l') {
0996: unreadByte();
0997: unreadByte();
0998: unreadByte();
0999: unreadByte();
1000: unreadByte();
1001: } else {
1002: CharBuffer directive = new CharBuffer();
1003:
1004: while ((ch = readByte()) >= 0 && ch != '?')
1005: directive.append((char) ch);
1006:
1007: String data = directive.toString().trim();
1008:
1009: if (data.startsWith("version")) {
1010: data = data.substring(7).trim();
1011: data = data.substring(1).trim(); // remove "="
1012: char quot = data.charAt(0);
1013: _version = data.substring(1, data.indexOf(quot, 1));
1014: data = data.substring(data.indexOf(quot, 1) + 1).trim();
1015: }
1016:
1017: if (data.startsWith("encoding")) {
1018: data = data.substring(8).trim();
1019: data = data.substring(1).trim(); // remove "="
1020: char quot = data.charAt(0);
1021: _encoding = data.substring(1, data.indexOf(quot, 1));
1022: data = data.substring(data.indexOf(quot, 1) + 1).trim();
1023:
1024: try {
1025: if (_is != null)
1026: _is.setEncoding(_encoding);
1027: } catch (java.io.UnsupportedEncodingException e) {
1028: throw new XMLStreamException(e);
1029: }
1030: }
1031:
1032: ch = readByte();
1033:
1034: if (ch != '>')
1035: throw error(L
1036: .l(
1037: "Expected '>' at end of '<?xml' declaration at {0}",
1038: charName(ch)));
1039: }
1040:
1041: skipWhitespace();
1042: unread();
1043: }
1044:
1045: /**
1046: * Reads and validate next character.
1047: */
1048: private void expect(int expect) throws XMLStreamException {
1049: int ch = read();
1050:
1051: if (ch != expect)
1052: throw error(L.l("expected {0} at {1}", charName(expect),
1053: charName(ch)));
1054: }
1055:
1056: /**
1057: * Reads a character.
1058: */
1059: private int read() throws XMLStreamException {
1060: if (_inputLength <= _inputOffset && !fillBuffer())
1061: return -1;
1062:
1063: int ch = _inputBuf[_inputOffset++];
1064:
1065: _ofs++;
1066:
1067: // XXX '\r'
1068: if (ch == '\n') {
1069: _row++;
1070: _lastCol = _col;
1071: _col = 1;
1072: } else
1073: _col++;
1074:
1075: return ch;
1076: }
1077:
1078: /**
1079: * Reads a character.
1080: */
1081: private void unread() {
1082: if (_inputOffset > 0) {
1083: _inputOffset--;
1084: _ofs--;
1085:
1086: if (_col > 1)
1087: _col--;
1088:
1089: if (_inputBuf[_inputOffset] == '\n') {
1090: _row--;
1091: _col = _lastCol;
1092: }
1093: }
1094: }
1095:
1096: /**
1097: * Reads a character.
1098: */
1099: private int readByte() throws XMLStreamException {
1100: try {
1101: if (_inputLength <= _inputOffset) {
1102: int ch = -1;
1103:
1104: if (_is != null)
1105: ch = _is.read();
1106: else if (_reader != null)
1107: ch = _reader.read();
1108:
1109: if (ch < 0)
1110: return ch;
1111:
1112: if (_inputBuf.length <= _inputLength)
1113: _inputLength = 0;
1114:
1115: _inputBuf[_inputLength++] = (char) ch;
1116: _inputOffset = _inputLength;
1117:
1118: _ofs++;
1119:
1120: // XXX '\r'
1121: if (ch == '\n') {
1122: _row++;
1123: _col = 1;
1124: } else
1125: _col++;
1126:
1127: return ch;
1128: } else
1129: return _inputBuf[_inputOffset++];
1130: } catch (IOException e) {
1131: throw new XMLStreamException(e);
1132: }
1133: }
1134:
1135: /**
1136: * Unreads a byte.
1137: */
1138: private void unreadByte() {
1139: if (_inputOffset > 0)
1140: _inputOffset--;
1141: }
1142:
1143: /**
1144: * Fills the input buffer.
1145: */
1146: private final boolean fillBuffer() throws XMLStreamException {
1147: try {
1148: if (_is != null) {
1149: _inputOffset = 0;
1150: _inputLength = _is.read(_inputBuf, 0, _inputBuf.length);
1151:
1152: return _inputLength > 0;
1153: } else if (_reader != null) {
1154: _inputOffset = 0;
1155: _inputLength = _reader.read(_inputBuf, 0,
1156: _inputBuf.length);
1157:
1158: return _inputLength > 0;
1159: } else {
1160: _inputOffset = 0;
1161: _inputLength = 0;
1162:
1163: return false;
1164: }
1165: } catch (IOException e) {
1166: throw new XMLStreamException(e);
1167: }
1168: }
1169:
1170: private String charName(int ch) {
1171: if (ch > 0x20 && ch <= 0x7f)
1172: return "'" + (char) ch + "'";
1173: else
1174: return "0x" + Integer.toHexString(ch);
1175: }
1176:
1177: private XMLStreamException error(String s) {
1178: return new XMLStreamException(location() + s);
1179: }
1180:
1181: private String location() {
1182: return ":" + _row + ":" + _col + " ";
1183: }
1184:
1185: public void close() throws XMLStreamException {
1186: TempCharBuffer tempCharBuffer = _tempCharBuffer;
1187: _tempCharBuffer = null;
1188: _cBuf = null;
1189:
1190: TempCharBuffer tempInputBuffer = _tempInputBuffer;
1191: _tempInputBuffer = null;
1192: _inputBuf = null;
1193:
1194: _inputOffset = _inputLength = 0;
1195:
1196: if (tempCharBuffer != null)
1197: TempCharBuffer.free(tempCharBuffer);
1198:
1199: if (tempInputBuffer != null)
1200: TempCharBuffer.free(tempInputBuffer);
1201:
1202: ReadStream is = _is;
1203: _is = null;
1204:
1205: if (is != null)
1206: is.close();
1207:
1208: Reader r = _reader;
1209: _reader = null;
1210:
1211: if (r != null) {
1212: try {
1213: r.close();
1214: } catch (IOException e) {
1215: throw new XMLStreamException(e);
1216: }
1217: }
1218: }
1219:
1220: static class RawName {
1221: char[] _buffer = new char[64];
1222: int _prefix;
1223: int _length;
1224:
1225: public QName resolve(NamespaceContext nsc) {
1226: if (getPrefix() == null)
1227: return new QName(nsc.getNamespaceURI(null),
1228: getLocalName());
1229: return new QName(nsc.getNamespaceURI(getPrefix()),
1230: getLocalName(), getPrefix());
1231: }
1232:
1233: public String toString() {
1234: return new String(_buffer, 0, _length);
1235: }
1236:
1237: String getLocalName() {
1238: return new String(_buffer, _prefix + 1, _length - _prefix
1239: - 1);
1240: }
1241:
1242: String getPrefix() {
1243: if (_prefix == -1)
1244: return null;
1245: return new String(_buffer, 0, _prefix);
1246: }
1247:
1248: void expandCapacity() {
1249: char[] newBuffer = new char[_buffer.length + 64];
1250:
1251: System.arraycopy(_buffer, 0, newBuffer, 0, _buffer.length);
1252:
1253: _buffer = newBuffer;
1254: }
1255: }
1256:
1257: /*
1258: static class NSContext {
1259:
1260: NSContext _parent;
1261:
1262: public NSContext(NSContext parent)
1263: {
1264: _parent = parent;
1265: }
1266: }
1267: */
1268: static {
1269: for (int i = 0; i < IS_XML_NAME.length; i++) {
1270: if (isXmlName(i) && i != ':')
1271: IS_XML_NAME[i] = isXmlName(i);
1272: }
1273: }
1274:
1275: private class StreamReaderLocation implements Location {
1276:
1277: private int _ofs;
1278: private int _row;
1279: private int _col;
1280:
1281: public StreamReaderLocation(int ofs, int row, int col) {
1282: this ._ofs = ofs;
1283: this ._row = row;
1284: this ._col = col;
1285: }
1286:
1287: public int getCharacterOffset() {
1288: return _ofs;
1289: }
1290:
1291: public int getColumnNumber() {
1292: return _col;
1293: }
1294:
1295: public int getLineNumber() {
1296: return _row;
1297: }
1298:
1299: public String getPublicId() {
1300: return _publicId;
1301: }
1302:
1303: public String getSystemId() {
1304: return _systemId;
1305: }
1306:
1307: public String toString() {
1308: return _row + ":" + _col;
1309: }
1310:
1311: }
1312:
1313: }
|