0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.xerces.xpointer;
0018:
0019: import java.util.Hashtable;
0020: import java.util.Vector;
0021:
0022: import org.apache.xerces.impl.Constants;
0023: import org.apache.xerces.impl.XMLErrorReporter;
0024: import org.apache.xerces.util.SymbolTable;
0025: import org.apache.xerces.util.XMLChar;
0026: import org.apache.xerces.util.XMLSymbols;
0027: import org.apache.xerces.xinclude.XIncludeHandler;
0028: import org.apache.xerces.xinclude.XIncludeNamespaceSupport;
0029: import org.apache.xerces.xni.Augmentations;
0030: import org.apache.xerces.xni.QName;
0031: import org.apache.xerces.xni.XMLAttributes;
0032: import org.apache.xerces.xni.XMLDTDHandler;
0033: import org.apache.xerces.xni.XMLDocumentHandler;
0034: import org.apache.xerces.xni.XMLString;
0035: import org.apache.xerces.xni.XNIException;
0036: import org.apache.xerces.xni.parser.XMLConfigurationException;
0037: import org.apache.xerces.xni.parser.XMLErrorHandler;
0038:
0039: /**
0040: * <p>
0041: * This is a pipeline component which extends the XIncludeHandler to perform
0042: * XPointer specific processing specified in the W3C XPointerFramework and
0043: * element() Scheme Recommendations.
0044: * </p>
0045: *
0046: * <p>
0047: * This component analyzes each event in the pipeline, looking for an element
0048: * that matches a PointerPart in the parent XInclude element's xpointer attribute
0049: * value. If the match succeeds, all children are passed by this component.
0050: * </p>
0051: *
0052: * <p>
0053: * See the <a href="http://www.w3.org/TR/xptr-framework//">XPointer Framework Recommendation</a> for
0054: * more information on the XPointer Framework and ShortHand Pointers.
0055: * See the <a href="http://www.w3.org/TR/xptr-element/">XPointer element() Scheme Recommendation</a> for
0056: * more information on the XPointer element() Scheme.
0057: * </p>
0058: *
0059: * @xerces.internal
0060: *
0061: * @version $Id: XPointerHandler.java 447248 2006-09-18 05:25:21Z mrglavas $
0062: */
0063: public final class XPointerHandler extends XIncludeHandler implements
0064: XPointerProcessor {
0065:
0066: // Fields
0067: // A Vector of XPointerParts
0068: protected Vector fXPointerParts = null;
0069:
0070: // The current XPointerPart
0071: protected XPointerPart fXPointerPart = null;
0072:
0073: // Has the fXPointerPart resolved successfully
0074: protected boolean fFoundMatchingPtrPart = false;
0075:
0076: // The XPointer Error reporter
0077: protected XMLErrorReporter fXPointerErrorReporter;
0078:
0079: // The XPointer Error Handler
0080: protected XMLErrorHandler fErrorHandler;
0081:
0082: // XPointerFramework symbol table
0083: protected SymbolTable fSymbolTable = null;
0084:
0085: // Supported schemes
0086: private final String ELEMENT_SCHEME_NAME = "element";
0087:
0088: // Has the XPointer resolved the subresource
0089: protected boolean fIsXPointerResolved = false;
0090:
0091: // Fixup xml:base and xml:lang attributes
0092: protected boolean fFixupBase = false;
0093: protected boolean fFixupLang = false;
0094:
0095: // ************************************************************************
0096: // Constructors
0097: // ************************************************************************
0098:
0099: /**
0100: *
0101: */
0102: public XPointerHandler() {
0103: super ();
0104:
0105: fXPointerParts = new Vector();
0106: fSymbolTable = new SymbolTable();
0107: }
0108:
0109: public XPointerHandler(SymbolTable symbolTable,
0110: XMLErrorHandler errorHandler, XMLErrorReporter errorReporter) {
0111: super ();
0112:
0113: fXPointerParts = new Vector();
0114: fSymbolTable = symbolTable;
0115: fErrorHandler = errorHandler;
0116: fXPointerErrorReporter = errorReporter;
0117: //fErrorReporter = errorReporter; // The XInclude ErrorReporter
0118: }
0119:
0120: public void setDocumentHandler(XMLDocumentHandler handler) {
0121: fDocumentHandler = handler;
0122: }
0123:
0124: // ************************************************************************
0125: // Implementation of the XPointerProcessor interface.
0126: // ************************************************************************
0127:
0128: /**
0129: * Parses the XPointer framework expression and delegates scheme specific parsing.
0130: *
0131: * @see org.apache.xerces.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
0132: */
0133: public void parseXPointer(String xpointer) throws XNIException {
0134:
0135: // Initialize
0136: init();
0137:
0138: // tokens
0139: final Tokens tokens = new Tokens(fSymbolTable);
0140:
0141: // scanner
0142: Scanner scanner = new Scanner(fSymbolTable) {
0143: protected void addToken(Tokens tokens, int token)
0144: throws XNIException {
0145: if (token == Tokens.XPTRTOKEN_OPEN_PAREN
0146: || token == Tokens.XPTRTOKEN_CLOSE_PAREN
0147: || token == Tokens.XPTRTOKEN_SCHEMENAME
0148: || token == Tokens.XPTRTOKEN_SCHEMEDATA
0149: || token == Tokens.XPTRTOKEN_SHORTHAND) {
0150: super .addToken(tokens, token);
0151: return;
0152: }
0153: reportError("InvalidXPointerToken",
0154: new Object[] { tokens.getTokenString(token) });
0155: }
0156: };
0157:
0158: // scan the XPointer expression
0159: int length = xpointer.length();
0160: boolean success = scanner.scanExpr(fSymbolTable, tokens,
0161: xpointer, 0, length);
0162:
0163: if (!success)
0164: reportError("InvalidXPointerExpression",
0165: new Object[] { xpointer });
0166:
0167: while (tokens.hasMore()) {
0168: int token = tokens.nextToken();
0169:
0170: switch (token) {
0171: case Tokens.XPTRTOKEN_SHORTHAND: {
0172:
0173: // The shortHand name
0174: token = tokens.nextToken();
0175: String shortHandPointerName = tokens
0176: .getTokenString(token);
0177:
0178: if (shortHandPointerName == null) {
0179: reportError("InvalidXPointerExpression",
0180: new Object[] { xpointer });
0181: }
0182:
0183: XPointerPart shortHandPointer = new ShortHandPointer(
0184: fSymbolTable);
0185: shortHandPointer.setSchemeName(shortHandPointerName);
0186: fXPointerParts.add(shortHandPointer);
0187: break;
0188: }
0189: case Tokens.XPTRTOKEN_SCHEMENAME: {
0190:
0191: // Retreive the local name and prefix to form the scheme name
0192: token = tokens.nextToken();
0193: String prefix = tokens.getTokenString(token);
0194: token = tokens.nextToken();
0195: String localName = tokens.getTokenString(token);
0196:
0197: String schemeName = prefix + localName;
0198:
0199: // The next character should be an open parenthesis
0200: int openParenCount = 0;
0201: int closeParenCount = 0;
0202:
0203: token = tokens.nextToken();
0204: String openParen = tokens.getTokenString(token);
0205: if (openParen != "XPTRTOKEN_OPEN_PAREN") {
0206:
0207: // can not have more than one ShortHand Pointer
0208: if (token == Tokens.XPTRTOKEN_SHORTHAND) {
0209: reportError("MultipleShortHandPointers",
0210: new Object[] { xpointer });
0211: } else {
0212: reportError("InvalidXPointerExpression",
0213: new Object[] { xpointer });
0214: }
0215: }
0216: openParenCount++;
0217:
0218: // followed by zero or more ( and the schemeData
0219: String schemeData = null;
0220: while (tokens.hasMore()) {
0221: token = tokens.nextToken();
0222: schemeData = tokens.getTokenString(token);
0223: if (schemeData != "XPTRTOKEN_OPEN_PAREN") {
0224: break;
0225: }
0226: openParenCount++;
0227: }
0228: token = tokens.nextToken();
0229: schemeData = tokens.getTokenString(token);
0230:
0231: // followed by the same number of )
0232: token = tokens.nextToken();
0233: String closeParen = tokens.getTokenString(token);
0234: if (closeParen != "XPTRTOKEN_CLOSE_PAREN") {
0235: reportError(
0236: "SchemeDataNotFollowedByCloseParenthesis",
0237: new Object[] { xpointer });
0238: }
0239: closeParenCount++;
0240:
0241: while (tokens.hasMore()) {
0242: if (tokens.getTokenString(tokens.peekToken()) != "XPTRTOKEN_OPEN_PAREN") {
0243: break;
0244: }
0245: closeParenCount++;
0246: }
0247:
0248: // check if the number of open parenthesis are equal to the number of close parenthesis
0249: if (openParenCount != closeParenCount) {
0250: reportError(
0251: "UnbalancedParenthesisInXPointerExpression",
0252: new Object[] { xpointer,
0253: new Integer(openParenCount),
0254: new Integer(closeParenCount) });
0255: }
0256:
0257: // Perform scheme specific parsing of the pointer part
0258: if (schemeName.equals(ELEMENT_SCHEME_NAME)) {
0259: XPointerPart elementSchemePointer = new ElementSchemePointer(
0260: fSymbolTable, fErrorReporter);
0261: elementSchemePointer.setSchemeName(schemeName);
0262: elementSchemePointer.setSchemeData(schemeData);
0263:
0264: // If an exception occurs while parsing the element() scheme expression
0265: // ignore it and move on to the next pointer part
0266: try {
0267: elementSchemePointer.parseXPointer(schemeData);
0268: fXPointerParts.add(elementSchemePointer);
0269: } catch (XNIException e) {
0270: // Re-throw the XPointer element() scheme syntax error.
0271: throw new XNIException(e);
0272: }
0273:
0274: } else {
0275: // ????
0276: reportWarning("SchemeUnsupported",
0277: new Object[] { schemeName });
0278: }
0279:
0280: break;
0281: }
0282: default:
0283: reportError("InvalidXPointerExpression",
0284: new Object[] { xpointer });
0285: }
0286: }
0287:
0288: }
0289:
0290: /**
0291: *
0292: * @see org.apache.xerces.xpointer.XPointerProcessor#resolveXPointer(org.apache.xerces.xni.QName, org.apache.xerces.xni.XMLAttributes, org.apache.xerces.xni.Augmentations, int event)
0293: */
0294: public boolean resolveXPointer(QName element,
0295: XMLAttributes attributes, Augmentations augs, int event)
0296: throws XNIException {
0297: boolean resolved = false;
0298:
0299: // The result of the first pointer part whose evaluation identifies
0300: // one or more subresources is reported by the XPointer processor as the
0301: // result of the pointer as a whole, and evaluation stops.
0302: // In our implementation, typically the first xpointer scheme that
0303: // matches an element is the document is considered.
0304: // If the pointer part resolved then use it, else search for the fragment
0305: // using next pointer part from lef-right.
0306: if (!fFoundMatchingPtrPart) {
0307:
0308: // for each element, attempt to resolve it against each pointer part
0309: // in the XPointer expression until a matching element is found.
0310: for (int i = 0; i < fXPointerParts.size(); i++) {
0311:
0312: fXPointerPart = (XPointerPart) fXPointerParts.get(i);
0313:
0314: if (fXPointerPart.resolveXPointer(element, attributes,
0315: augs, event)) {
0316: fFoundMatchingPtrPart = true;
0317: resolved = true;
0318: }
0319: }
0320: } else {
0321: if (fXPointerPart.resolveXPointer(element, attributes,
0322: augs, event)) {
0323: resolved = true;
0324: }
0325: }
0326:
0327: if (!fIsXPointerResolved) {
0328: fIsXPointerResolved = resolved;
0329: }
0330:
0331: return resolved;
0332: }
0333:
0334: /**
0335: * Returns true if the Node fragment is resolved.
0336: *
0337: * @see org.apache.xerces.xpointer.XPointerProcessor#isFragmentResolved()
0338: */
0339: public boolean isFragmentResolved() throws XNIException {
0340: boolean resolved = (fXPointerPart != null) ? fXPointerPart
0341: .isFragmentResolved() : false;
0342:
0343: if (!fIsXPointerResolved) {
0344: fIsXPointerResolved = resolved;
0345: }
0346:
0347: return resolved;
0348: }
0349:
0350: /**
0351: * Returns true if the XPointer expression resolves to a non-element child
0352: * of the current resource fragment.
0353: *
0354: * @see org.apache.xerces.xpointer.XPointerPart#isChildFragmentResolved()
0355: *
0356: */
0357: public boolean isChildFragmentResolved() throws XNIException {
0358: boolean resolved = (fXPointerPart != null) ? fXPointerPart
0359: .isChildFragmentResolved() : false;
0360: return resolved;
0361: }
0362:
0363: /**
0364: * Returns true if the XPointer successfully found a sub-resource .
0365: *
0366: * @see org.apache.xerces.xpointer.XPointerProcessor#isFragmentResolved()
0367: */
0368: public boolean isXPointerResolved() throws XNIException {
0369: return fIsXPointerResolved;
0370: }
0371:
0372: /**
0373: * Returns the pointer part used to resolve the document fragment.
0374: *
0375: * @return String - The pointer part used to resolve the document fragment.
0376: */
0377: public XPointerPart getXPointerPart() {
0378: return fXPointerPart;
0379: }
0380:
0381: /**
0382: * Reports XPointer Errors
0383: *
0384: */
0385: private void reportError(String key, Object[] arguments)
0386: throws XNIException {
0387: /*
0388: fXPointerErrorReporter.reportError(
0389: XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
0390: XMLErrorReporter.SEVERITY_ERROR);
0391: */
0392: throw new XNIException(
0393: (fErrorReporter
0394: .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
0395: .formatMessage(fErrorReporter.getLocale(), key,
0396: arguments));
0397: }
0398:
0399: /**
0400: * Reports XPointer Warnings
0401: *
0402: */
0403: private void reportWarning(String key, Object[] arguments)
0404: throws XNIException {
0405: fXPointerErrorReporter.reportError(
0406: XPointerMessageFormatter.XPOINTER_DOMAIN, key,
0407: arguments, XMLErrorReporter.SEVERITY_WARNING);
0408: }
0409:
0410: /**
0411: * Initializes error handling objects
0412: *
0413: */
0414: protected void initErrorReporter() {
0415: if (fXPointerErrorReporter == null) {
0416: fXPointerErrorReporter = new XMLErrorReporter();
0417: }
0418: if (fErrorHandler == null) {
0419: fErrorHandler = new XPointerErrorHandler();
0420: }
0421: /*
0422: fXPointerErrorReporter.setProperty(Constants.XERCES_PROPERTY_PREFIX
0423: + Constants.ERROR_HANDLER_PROPERTY, fErrorHandler);
0424: */
0425: fXPointerErrorReporter.putMessageFormatter(
0426: XPointerMessageFormatter.XPOINTER_DOMAIN,
0427: new XPointerMessageFormatter());
0428: }
0429:
0430: /**
0431: * Initializes the XPointer Processor;
0432: */
0433: protected void init() {
0434: fXPointerParts.clear();
0435: fXPointerPart = null;
0436: fFoundMatchingPtrPart = false;
0437: fIsXPointerResolved = false;
0438: //fFixupBase = false;
0439: //fFixupLang = false;
0440:
0441: initErrorReporter();
0442: }
0443:
0444: /**
0445: * Returns a Vector of XPointerPart objects
0446: *
0447: * @return A Vector of XPointerPart objects.
0448: */
0449: public Vector getPointerParts() {
0450: return fXPointerParts;
0451: }
0452:
0453: /**
0454: * List of XPointer Framework tokens.
0455: *
0456: * @xerces.internal
0457: *
0458: */
0459: private final class Tokens {
0460:
0461: /**
0462: * XPointer Framework tokens
0463: * [1] Pointer ::= Shorthand | SchemeBased
0464: * [2] Shorthand ::= NCName
0465: * [3] SchemeBased ::= PointerPart (S? PointerPart)*
0466: * [4] PointerPart ::= SchemeName '(' SchemeData ')'
0467: * [5] SchemeName ::= QName
0468: * [6] SchemeData ::= EscapedData*
0469: * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
0470: * [8] NormalChar ::= UnicodeChar - [()^]
0471: * [9] UnicodeChar ::= [#x0-#x10FFFF]
0472: *
0473: */
0474: private static final int XPTRTOKEN_OPEN_PAREN = 0,
0475: XPTRTOKEN_CLOSE_PAREN = 1, XPTRTOKEN_SHORTHAND = 2,
0476: XPTRTOKEN_SCHEMENAME = 3, XPTRTOKEN_SCHEMEDATA = 4;
0477:
0478: // Token names
0479: private final String[] fgTokenNames = { "XPTRTOKEN_OPEN_PAREN",
0480: "XPTRTOKEN_CLOSE_PAREN", "XPTRTOKEN_SHORTHAND",
0481: "XPTRTOKEN_SCHEMENAME", "XPTRTOKEN_SCHEMEDATA" };
0482:
0483: // Token count
0484: private static final int INITIAL_TOKEN_COUNT = 1 << 8;
0485:
0486: private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
0487:
0488: private int fTokenCount = 0;
0489:
0490: // Current token position
0491: private int fCurrentTokenIndex;
0492:
0493: private SymbolTable fSymbolTable;
0494:
0495: private Hashtable fTokenNames = new Hashtable();
0496:
0497: /**
0498: * Constructor
0499: *
0500: * @param symbolTable SymbolTable
0501: */
0502: private Tokens(SymbolTable symbolTable) {
0503: fSymbolTable = symbolTable;
0504:
0505: fTokenNames.put(new Integer(XPTRTOKEN_OPEN_PAREN),
0506: "XPTRTOKEN_OPEN_PAREN");
0507: fTokenNames.put(new Integer(XPTRTOKEN_CLOSE_PAREN),
0508: "XPTRTOKEN_CLOSE_PAREN");
0509: fTokenNames.put(new Integer(XPTRTOKEN_SHORTHAND),
0510: "XPTRTOKEN_SHORTHAND");
0511: fTokenNames.put(new Integer(XPTRTOKEN_SCHEMENAME),
0512: "XPTRTOKEN_SCHEMENAME");
0513: fTokenNames.put(new Integer(XPTRTOKEN_SCHEMEDATA),
0514: "XPTRTOKEN_SCHEMEDATA");
0515: }
0516:
0517: /**
0518: * Returns the token String
0519: * @param token The index of the token
0520: * @return String The token string
0521: */
0522: private String getTokenString(int token) {
0523: return (String) fTokenNames.get(new Integer(token));
0524: }
0525:
0526: /**
0527: * Add the specified string as a token
0528: *
0529: * @param token The token string
0530: */
0531: private void addToken(String tokenStr) {
0532: Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
0533: if (tokenInt == null) {
0534: tokenInt = new Integer(fTokenNames.size());
0535: fTokenNames.put(tokenInt, tokenStr);
0536: }
0537: addToken(tokenInt.intValue());
0538: }
0539:
0540: /**
0541: * Add the specified int token
0542: *
0543: * @param token The int specifying the token
0544: */
0545: private void addToken(int token) {
0546: try {
0547: fTokens[fTokenCount] = token;
0548: } catch (ArrayIndexOutOfBoundsException ex) {
0549: int[] oldList = fTokens;
0550: fTokens = new int[fTokenCount << 1];
0551: System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
0552: fTokens[fTokenCount] = token;
0553: }
0554: fTokenCount++;
0555: }
0556:
0557: /**
0558: * Resets the current position to the head of the token list.
0559: */
0560: private void rewind() {
0561: fCurrentTokenIndex = 0;
0562: }
0563:
0564: /**
0565: * Returns true if the {@link #getNextToken()} method
0566: * returns a valid token.
0567: */
0568: private boolean hasMore() {
0569: return fCurrentTokenIndex < fTokenCount;
0570: }
0571:
0572: /**
0573: * Obtains the token at the current position, then advance
0574: * the current position by one.
0575: *
0576: * throws If there's no such next token, this method throws
0577: * <tt>new XNIException("XPointerProcessingError");</tt>.
0578: */
0579: private int nextToken() throws XNIException {
0580: if (fCurrentTokenIndex == fTokenCount) {
0581: reportError("XPointerProcessingError", null);
0582: }
0583: return fTokens[fCurrentTokenIndex++];
0584: }
0585:
0586: /**
0587: * Obtains the token at the current position, without advancing
0588: * the current position.
0589: *
0590: * If there's no such next token, this method throws
0591: * <tt>new XNIException("XPointerProcessingError");</tt>.
0592: */
0593: private int peekToken() throws XNIException {
0594: if (fCurrentTokenIndex == fTokenCount) {
0595: reportError("XPointerProcessingError", null);
0596: }
0597: return fTokens[fCurrentTokenIndex];
0598: }
0599:
0600: /**
0601: * Obtains the token at the current position as a String.
0602: *
0603: * If there's no current token or if the current token
0604: * is not a string token, this method throws
0605: * If there's no such next token, this method throws
0606: * <tt>new XNIException("XPointerProcessingError");</tt>.
0607: */
0608: private String nextTokenAsString() throws XNIException {
0609: String tokenStrint = getTokenString(nextToken());
0610: if (tokenStrint == null) {
0611: reportError("XPointerProcessingError", null);
0612: }
0613: return tokenStrint;
0614: }
0615: }
0616:
0617: /**
0618: *
0619: * The XPointer expression scanner. Scans the XPointer framework expression.
0620: *
0621: * @xerces.internal
0622: *
0623: */
0624: private class Scanner {
0625:
0626: /**
0627: * 7-bit ASCII subset
0628: *
0629: * 0 1 2 3 4 5 6 7 8 9 A B C D E F
0630: * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
0631: * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
0632: * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
0633: * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
0634: * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
0635: * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
0636: * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
0637: * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
0638: */
0639: private static final byte CHARTYPE_INVALID = 0, // invalid XML character
0640: CHARTYPE_OTHER = 1, // not special - one of "#%&;?\`{}~" or DEL
0641: CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
0642: CHARTYPE_CARRET = 3, // ^
0643: CHARTYPE_OPEN_PAREN = 4, // '(' (0x28)
0644: CHARTYPE_CLOSE_PAREN = 5, // ')' (0x29)
0645: CHARTYPE_MINUS = 6, // '-' (0x2D)
0646: CHARTYPE_PERIOD = 7, // '.' (0x2E)
0647: CHARTYPE_SLASH = 8, // '/' (0x2F)
0648: CHARTYPE_DIGIT = 9, // '0'-'9' (0x30 to 0x39)
0649: CHARTYPE_COLON = 10, // ':' (0x3A)
0650: CHARTYPE_EQUAL = 11, // '=' (0x3D)
0651: CHARTYPE_LETTER = 12, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
0652: CHARTYPE_UNDERSCORE = 13, // '_' (0x5F)
0653: CHARTYPE_NONASCII = 14; // Non-ASCII Unicode codepoint (>= 0x80)
0654:
0655: private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0,
0656: 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0657: 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1,
0658: 1, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 1, 1, 11,
0659: 1, 1, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
0660: 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
0661: 12, 1, 1, 1, 3, 13, 1, 12, 12, 12, 12, 12, 12, 12, 12,
0662: 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
0663: 12, 12, 12, 12, 1, 1, 1, 1, 1 };
0664:
0665: //
0666: // Data
0667: //
0668: /** Symbol table. */
0669: private SymbolTable fSymbolTable;
0670:
0671: /**
0672: * Constructs an XPointer Framework expression scanner.
0673: *
0674: * @param symbolTable SymbolTable
0675: */
0676: private Scanner(SymbolTable symbolTable) {
0677: // save pool and tokens
0678: fSymbolTable = symbolTable;
0679:
0680: } // <init>(SymbolTable)
0681:
0682: /**
0683: * Scans the XPointer Expression
0684: *
0685: */
0686: private boolean scanExpr(SymbolTable symbolTable,
0687: Tokens tokens, String data, int currentOffset,
0688: int endOffset) throws XNIException {
0689:
0690: int ch;
0691: int openParen = 0;
0692: int closeParen = 0;
0693: int nameOffset, dataOffset;
0694: boolean isQName = false;
0695: String name = null;
0696: String prefix = null;
0697: String schemeData = null;
0698: StringBuffer schemeDataBuff = new StringBuffer();
0699:
0700: while (true) {
0701:
0702: if (currentOffset == endOffset) {
0703: break;
0704: }
0705: ch = data.charAt(currentOffset);
0706:
0707: //
0708: while (ch == ' ' || ch == 0x0A || ch == 0x09
0709: || ch == 0x0D) {
0710: if (++currentOffset == endOffset) {
0711: break;
0712: }
0713: ch = data.charAt(currentOffset);
0714: }
0715: if (currentOffset == endOffset) {
0716: break;
0717: }
0718:
0719: //
0720: // [1] Pointer ::= Shorthand | SchemeBased
0721: // [2] Shorthand ::= NCName
0722: // [3] SchemeBased ::= PointerPart (S? PointerPart)*
0723: // [4] PointerPart ::= SchemeName '(' SchemeData ')'
0724: // [5] SchemeName ::= QName
0725: // [6] SchemeData ::= EscapedData*
0726: // [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
0727: // [8] NormalChar ::= UnicodeChar - [()^]
0728: // [9] UnicodeChar ::= [#x0-#x10FFFF]
0729: // [?] QName ::= (NCName ':')? NCName
0730: // [?] NCName ::= (Letter | '_') (NCNameChar)*
0731: // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
0732: // [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
0733: // [?] Digit ::= [0-9] (ascii subset of 'Digit')
0734: //
0735: byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
0736: : fASCIICharMap[ch];
0737:
0738: switch (chartype) {
0739:
0740: case CHARTYPE_OPEN_PAREN: // '('
0741: addToken(tokens, Tokens.XPTRTOKEN_OPEN_PAREN);
0742: openParen++;
0743: ++currentOffset;
0744: break;
0745:
0746: case CHARTYPE_CLOSE_PAREN: // ')'
0747: addToken(tokens, Tokens.XPTRTOKEN_CLOSE_PAREN);
0748: closeParen++;
0749: ++currentOffset;
0750: break;
0751:
0752: case CHARTYPE_CARRET:
0753: case CHARTYPE_COLON:
0754: case CHARTYPE_DIGIT:
0755: case CHARTYPE_EQUAL:
0756: case CHARTYPE_LETTER:
0757: case CHARTYPE_MINUS:
0758: case CHARTYPE_NONASCII:
0759: case CHARTYPE_OTHER:
0760: case CHARTYPE_PERIOD:
0761: case CHARTYPE_SLASH:
0762: case CHARTYPE_UNDERSCORE:
0763: case CHARTYPE_WHITESPACE:
0764: // Scanning SchemeName | Shorthand
0765: if (openParen == 0) {
0766: nameOffset = currentOffset;
0767: currentOffset = scanNCName(data, endOffset,
0768: currentOffset);
0769:
0770: if (currentOffset == nameOffset) {
0771: reportError("InvalidShortHandPointer",
0772: new Object[] { data });
0773: return false;
0774: }
0775:
0776: if (currentOffset < endOffset) {
0777: ch = data.charAt(currentOffset);
0778: } else {
0779: ch = -1;
0780: }
0781:
0782: name = symbolTable.addSymbol(data.substring(
0783: nameOffset, currentOffset));
0784: prefix = XMLSymbols.EMPTY_STRING;
0785:
0786: // The name is a QName => a SchemeName
0787: if (ch == ':') {
0788: if (++currentOffset == endOffset) {
0789: return false;
0790: }
0791:
0792: ch = data.charAt(currentOffset);
0793: prefix = name;
0794: nameOffset = currentOffset;
0795: currentOffset = scanNCName(data, endOffset,
0796: currentOffset);
0797:
0798: if (currentOffset == nameOffset) {
0799: return false;
0800: }
0801:
0802: if (currentOffset < endOffset) {
0803: ch = data.charAt(currentOffset);
0804: } else {
0805: ch = -1;
0806: }
0807:
0808: isQName = true;
0809: name = symbolTable.addSymbol(data
0810: .substring(nameOffset,
0811: currentOffset));
0812: }
0813:
0814: // REVISIT:
0815: if (currentOffset != endOffset) {
0816: addToken(tokens,
0817: Tokens.XPTRTOKEN_SCHEMENAME);
0818: tokens.addToken(prefix);
0819: tokens.addToken(name);
0820: isQName = false;
0821: } else if (currentOffset == endOffset) {
0822: // NCName => Shorthand
0823: addToken(tokens, Tokens.XPTRTOKEN_SHORTHAND);
0824: tokens.addToken(name);
0825: isQName = false;
0826: }
0827:
0828: // reset open/close paren for the next pointer part
0829: closeParen = 0;
0830:
0831: break;
0832:
0833: } else if (openParen > 0 && closeParen == 0
0834: && name != null) {
0835: // Scanning SchemeData
0836: dataOffset = currentOffset;
0837: currentOffset = scanData(data, schemeDataBuff,
0838: endOffset, currentOffset);
0839:
0840: if (currentOffset == dataOffset) {
0841: reportError("InvalidSchemeDataInXPointer",
0842: new Object[] { data });
0843: return false;
0844: }
0845:
0846: if (currentOffset < endOffset) {
0847: ch = data.charAt(currentOffset);
0848: } else {
0849: ch = -1;
0850: }
0851:
0852: schemeData = symbolTable
0853: .addSymbol(schemeDataBuff.toString());
0854: addToken(tokens, Tokens.XPTRTOKEN_SCHEMEDATA);
0855: tokens.addToken(schemeData);
0856:
0857: // reset open/close paren for the next pointer part
0858: openParen = 0;
0859: schemeDataBuff.delete(0, schemeDataBuff
0860: .length());
0861:
0862: } else {
0863: // ex. schemeName()
0864: // Should we throw an exception with a more suitable message instead??
0865: return false;
0866: }
0867: }
0868: } // end while
0869: return true;
0870: }
0871:
0872: /**
0873: * Scans a NCName.
0874: * From Namespaces in XML
0875: * [5] NCName ::= (Letter | '_') (NCNameChar)*
0876: * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
0877: *
0878: * @param data A String containing the XPointer expression
0879: * @param endOffset The int XPointer expression length
0880: * @param currentOffset An int representing the current position of the XPointer expression pointer
0881: */
0882: private int scanNCName(String data, int endOffset,
0883: int currentOffset) {
0884: int ch = data.charAt(currentOffset);
0885: if (ch >= 0x80) {
0886: if (!XMLChar.isNameStart(ch)) {
0887: return currentOffset;
0888: }
0889: } else {
0890: byte chartype = fASCIICharMap[ch];
0891: if (chartype != CHARTYPE_LETTER
0892: && chartype != CHARTYPE_UNDERSCORE) {
0893: return currentOffset;
0894: }
0895: }
0896:
0897: //while (currentOffset++ < endOffset) {
0898: while (++currentOffset < endOffset) {
0899: ch = data.charAt(currentOffset);
0900: if (ch >= 0x80) {
0901: if (!XMLChar.isName(ch)) {
0902: break;
0903: }
0904: } else {
0905: byte chartype = fASCIICharMap[ch];
0906: if (chartype != CHARTYPE_LETTER
0907: && chartype != CHARTYPE_DIGIT
0908: && chartype != CHARTYPE_PERIOD
0909: && chartype != CHARTYPE_MINUS
0910: && chartype != CHARTYPE_UNDERSCORE) {
0911: break;
0912: }
0913: }
0914: }
0915: return currentOffset;
0916: }
0917:
0918: /**
0919: * Scans the SchemeData.
0920: * [6] SchemeData ::= EscapedData*
0921: * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
0922: * [8] NormalChar ::= UnicodeChar - [()^]
0923: * [9] UnicodeChar ::= [#x0-#x10FFFF]
0924: *
0925: */
0926: private int scanData(String data, StringBuffer schemeData,
0927: int endOffset, int currentOffset) {
0928: while (true) {
0929:
0930: if (currentOffset == endOffset) {
0931: break;
0932: }
0933:
0934: int ch = data.charAt(currentOffset);
0935: byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
0936: : fASCIICharMap[ch];
0937:
0938: if (chartype == CHARTYPE_OPEN_PAREN) {
0939: schemeData.append(ch);
0940: //schemeData.append(Tokens.XPTRTOKEN_OPEN_PAREN);
0941: currentOffset = scanData(data, schemeData,
0942: endOffset, ++currentOffset);
0943: if (currentOffset == endOffset) {
0944: return currentOffset;
0945: }
0946:
0947: ch = data.charAt(currentOffset);
0948: chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
0949: : fASCIICharMap[ch];
0950:
0951: if (chartype != CHARTYPE_CLOSE_PAREN) {
0952: return endOffset;
0953: }
0954: schemeData.append((char) ch);
0955: ++currentOffset;//
0956:
0957: } else if (chartype == CHARTYPE_CLOSE_PAREN) {
0958: return currentOffset;
0959:
0960: } else if (chartype == CHARTYPE_CARRET) {
0961: ch = data.charAt(++currentOffset);
0962: chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
0963: : fASCIICharMap[ch];
0964:
0965: if (chartype != CHARTYPE_CARRET
0966: && chartype != CHARTYPE_OPEN_PAREN
0967: && chartype != CHARTYPE_CLOSE_PAREN) {
0968: break;
0969: }
0970: schemeData.append((char) ch);
0971: ++currentOffset;
0972:
0973: } else {
0974: schemeData.append((char) ch);
0975: ++currentOffset;//
0976: }
0977: }
0978:
0979: return currentOffset;
0980: }
0981:
0982: //
0983: // Protected methods
0984: //
0985:
0986: /**
0987: * This method adds the specified token to the token list. By
0988: * default, this method allows all tokens. However, subclasses
0989: * of the XPathExprScanner can override this method in order
0990: * to disallow certain tokens from being used in the scanned
0991: * XPath expression. This is a convenient way of allowing only
0992: * a subset of XPath.
0993: */
0994: protected void addToken(Tokens tokens, int token)
0995: throws XNIException {
0996: tokens.addToken(token);
0997: } // addToken(int)
0998:
0999: } // class Scanner
1000:
1001: // ************************************************************************
1002: // Overridden XMLDocumentHandler methods
1003: // ************************************************************************
1004: /**
1005: * If the comment is a child of a matched element, then pass else return.
1006: *
1007: * @param text The text in the comment.
1008: * @param augs Additional information that may include infoset augmentations
1009: *
1010: * @exception XNIException
1011: * Thrown by application to signal an error.
1012: */
1013: public void comment(XMLString text, Augmentations augs)
1014: throws XNIException {
1015: if (!isChildFragmentResolved()) {
1016: return;
1017: }
1018: super .comment(text, augs);
1019: }
1020:
1021: /**
1022: * A processing instruction. Processing instructions consist of a
1023: * target name and, optionally, text data. The data is only meaningful
1024: * to the application.
1025: * <p>
1026: * Typically, a processing instruction's data will contain a series
1027: * of pseudo-attributes. These pseudo-attributes follow the form of
1028: * element attributes but are <strong>not</strong> parsed or presented
1029: * to the application as anything other than text. The application is
1030: * responsible for parsing the data.
1031: *
1032: * @param target The target.
1033: * @param data The data or null if none specified.
1034: * @param augs Additional information that may include infoset augmentations
1035: *
1036: * @exception XNIException
1037: * Thrown by handler to signal an error.
1038: */
1039: public void processingInstruction(String target, XMLString data,
1040: Augmentations augs) throws XNIException {
1041: if (!isChildFragmentResolved()) {
1042: return;
1043: }
1044: super .processingInstruction(target, data, augs);
1045: }
1046:
1047: /**
1048: * The start of an element.
1049: *
1050: * @param element The name of the element.
1051: * @param attributes The element attributes.
1052: * @param augs Additional information that may include infoset augmentations
1053: *
1054: * @exception XNIException
1055: * Thrown by handler to signal an error.
1056: */
1057: public void startElement(QName element, XMLAttributes attributes,
1058: Augmentations augs) throws XNIException {
1059: if (!resolveXPointer(element, attributes, augs,
1060: XPointerPart.EVENT_ELEMENT_START)) {
1061:
1062: // xml:base and xml:lang processing
1063: if (fFixupBase) {
1064: processXMLBaseAttributes(attributes);
1065: }
1066: if (fFixupLang) {
1067: processXMLLangAttributes(attributes);
1068: }
1069:
1070: // set the context invalid if the element till an element from the result infoset is included
1071: fNamespaceContext.setContextInvalid();
1072:
1073: return;
1074: }
1075: super .startElement(element, attributes, augs);
1076: }
1077:
1078: /**
1079: * An empty element.
1080: *
1081: * @param element The name of the element.
1082: * @param attributes The element attributes.
1083: * @param augs Additional information that may include infoset augmentations
1084: *
1085: * @exception XNIException
1086: * Thrown by handler to signal an error.
1087: */
1088: public void emptyElement(QName element, XMLAttributes attributes,
1089: Augmentations augs) throws XNIException {
1090: if (!resolveXPointer(element, attributes, augs,
1091: XPointerPart.EVENT_ELEMENT_EMPTY)) {
1092: // xml:base and xml:lang processing
1093: if (fFixupBase) {
1094: processXMLBaseAttributes(attributes);
1095: }
1096: if (fFixupLang) {
1097: processXMLLangAttributes(attributes);
1098: }
1099: // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1100:
1101: // set the context invalid if the element till an element from the result infoset is included
1102: fNamespaceContext.setContextInvalid();
1103: return;
1104: }
1105: super .emptyElement(element, attributes, augs);
1106: }
1107:
1108: /**
1109: * Character content.
1110: *
1111: * @param text The content.
1112: * @param augs Additional information that may include infoset augmentations
1113: *
1114: * @exception XNIException
1115: * Thrown by handler to signal an error.
1116: */
1117: public void characters(XMLString text, Augmentations augs)
1118: throws XNIException {
1119: if (!isChildFragmentResolved()) {
1120: return;
1121: }
1122: super .characters(text, augs);
1123: }
1124:
1125: /**
1126: * Ignorable whitespace. For this method to be called, the document
1127: * source must have some way of determining that the text containing
1128: * only whitespace characters should be considered ignorable. For
1129: * example, the validator can determine if a length of whitespace
1130: * characters in the document are ignorable based on the element
1131: * content model.
1132: *
1133: * @param text The ignorable whitespace.
1134: * @param augs Additional information that may include infoset augmentations
1135: *
1136: * @exception XNIException
1137: * Thrown by handler to signal an error.
1138: */
1139: public void ignorableWhitespace(XMLString text, Augmentations augs)
1140: throws XNIException {
1141: if (!isChildFragmentResolved()) {
1142: return;
1143: }
1144: super .ignorableWhitespace(text, augs);
1145: }
1146:
1147: /**
1148: * The end of an element.
1149: *
1150: * @param element The name of the element.
1151: * @param augs Additional information that may include infoset augmentations
1152: *
1153: * @exception XNIException
1154: * Thrown by handler to signal an error.
1155: */
1156: public void endElement(QName element, Augmentations augs)
1157: throws XNIException {
1158: if (!resolveXPointer(element, null, augs,
1159: XPointerPart.EVENT_ELEMENT_END)) {
1160:
1161: // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1162: return;
1163: }
1164: super .endElement(element, augs);
1165: }
1166:
1167: /**
1168: * The start of a CDATA section.
1169: *
1170: * @param augs Additional information that may include infoset augmentations
1171: *
1172: * @exception XNIException
1173: * Thrown by handler to signal an error.
1174: */
1175: public void startCDATA(Augmentations augs) throws XNIException {
1176: if (!isChildFragmentResolved()) {
1177: return;
1178: }
1179: super .startCDATA(augs);
1180: }
1181:
1182: /**
1183: * The end of a CDATA section.
1184: *
1185: * @param augs Additional information that may include infoset augmentations
1186: *
1187: * @exception XNIException
1188: * Thrown by handler to signal an error.
1189: */
1190: public void endCDATA(Augmentations augs) throws XNIException {
1191: if (!isChildFragmentResolved()) {
1192: return;
1193: }
1194: super .endCDATA(augs);
1195: }
1196:
1197: // ************************************************************************
1198: // Overridden XMLComponent methods
1199: // ************************************************************************
1200: /**
1201: * <p>
1202: * Sets the value of a property. This method is called by the component
1203: * manager any time after reset when a property changes value.
1204: * </p>
1205: * <strong>Note:</strong> Components should silently ignore properties
1206: * that do not affect the operation of the component.
1207: *
1208: * @param propertyId The property identifier.
1209: * @param value The value of the property.
1210: *
1211: * @throws XMLConfigurationException Thrown for configuration error.
1212: * In general, components should
1213: * only throw this exception if
1214: * it is <strong>really</strong>
1215: * a critical error.
1216: */
1217: public void setProperty(String propertyId, Object value)
1218: throws XMLConfigurationException {
1219:
1220: // Error reporter
1221: if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1222: + Constants.ERROR_REPORTER_PROPERTY) {
1223: if (value != null) {
1224: fXPointerErrorReporter = (XMLErrorReporter) value;
1225: } else {
1226: fXPointerErrorReporter = null;
1227: }
1228: }
1229:
1230: // Error handler
1231: if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1232: + Constants.ERROR_HANDLER_PROPERTY) {
1233: if (value != null) {
1234: fErrorHandler = (XMLErrorHandler) value;
1235: } else {
1236: fErrorHandler = null;
1237: }
1238: }
1239:
1240: // xml:lang
1241: if (propertyId == Constants.XERCES_FEATURE_PREFIX
1242: + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE) {
1243: if (value != null) {
1244: fFixupLang = ((Boolean) value).booleanValue();
1245: } else {
1246: fFixupLang = false;
1247: }
1248: }
1249:
1250: // xml:base
1251: if (propertyId == Constants.XERCES_FEATURE_PREFIX
1252: + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE) {
1253: if (value != null) {
1254: fFixupBase = ((Boolean) value).booleanValue();
1255: } else {
1256: fFixupBase = false;
1257: }
1258: }
1259:
1260: //
1261: if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1262: + Constants.NAMESPACE_CONTEXT_PROPERTY) {
1263: fNamespaceContext = (XIncludeNamespaceSupport) value;
1264: }
1265:
1266: super.setProperty(propertyId, value);
1267: }
1268:
1269: }
|