You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1249 lines
46 KiB
1249 lines
46 KiB
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
/*
|
|
* Copyright 2005 The Apache Software Foundation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.sun.org.apache.xerces.internal.xpointer;
|
|
|
|
import java.util.Hashtable;
|
|
import java.util.Vector;
|
|
|
|
import com.sun.org.apache.xerces.internal.impl.Constants;
|
|
import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
|
|
import com.sun.org.apache.xerces.internal.util.SymbolTable;
|
|
import com.sun.org.apache.xerces.internal.util.XMLChar;
|
|
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
|
|
import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
|
|
import com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport;
|
|
import com.sun.org.apache.xerces.internal.xni.Augmentations;
|
|
import com.sun.org.apache.xerces.internal.xni.QName;
|
|
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
|
|
import com.sun.org.apache.xerces.internal.xni.XMLString;
|
|
import com.sun.org.apache.xerces.internal.xni.XNIException;
|
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
|
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
|
|
|
|
/**
|
|
* <p>
|
|
* This is a pipeline component which extends the XIncludeHandler to perform
|
|
* XPointer specific processing specified in the W3C XPointerFramework and
|
|
* element() Scheme Recommendations.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* This component analyzes each event in the pipeline, looking for an element
|
|
* that matches a PointerPart in the parent XInclude element's xpointer attribute
|
|
* value. If the match succeeds, all children are passed by this component.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* See the <a href="http://www.w3.org/TR/xptr-framework//">XPointer Framework Recommendation</a> for
|
|
* more information on the XPointer Framework and ShortHand Pointers.
|
|
* See the <a href="http://www.w3.org/TR/xptr-element/">XPointer element() Scheme Recommendation</a> for
|
|
* more information on the XPointer element() Scheme.
|
|
* </p>
|
|
*
|
|
* @xerces.internal
|
|
*
|
|
*/
|
|
public final class XPointerHandler extends XIncludeHandler implements
|
|
XPointerProcessor {
|
|
|
|
// Fields
|
|
// A Vector of XPointerParts
|
|
protected Vector fXPointerParts = null;
|
|
|
|
// The current XPointerPart
|
|
protected XPointerPart fXPointerPart = null;
|
|
|
|
// Has the fXPointerPart resolved successfully
|
|
protected boolean fFoundMatchingPtrPart = false;
|
|
|
|
// The XPointer Error reporter
|
|
protected XMLErrorReporter fXPointerErrorReporter;
|
|
|
|
// The XPointer Error Handler
|
|
protected XMLErrorHandler fErrorHandler;
|
|
|
|
// XPointerFramework symbol table
|
|
protected SymbolTable fSymbolTable = null;
|
|
|
|
// Supported schemes
|
|
private final String ELEMENT_SCHEME_NAME = "element";
|
|
|
|
// Has the XPointer resolved the subresource
|
|
protected boolean fIsXPointerResolved = false;
|
|
|
|
// Fixup xml:base and xml:lang attributes
|
|
protected boolean fFixupBase = false;
|
|
protected boolean fFixupLang = false;
|
|
|
|
// ************************************************************************
|
|
// Constructors
|
|
// ************************************************************************
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public XPointerHandler() {
|
|
super();
|
|
|
|
fXPointerParts = new Vector();
|
|
fSymbolTable = new SymbolTable();
|
|
}
|
|
|
|
public XPointerHandler(SymbolTable symbolTable,
|
|
XMLErrorHandler errorHandler, XMLErrorReporter errorReporter) {
|
|
super();
|
|
|
|
fXPointerParts = new Vector();
|
|
fSymbolTable = symbolTable;
|
|
fErrorHandler = errorHandler;
|
|
fXPointerErrorReporter = errorReporter;
|
|
//fErrorReporter = errorReporter; // The XInclude ErrorReporter
|
|
}
|
|
|
|
// ************************************************************************
|
|
// Implementation of the XPointerProcessor interface.
|
|
// ************************************************************************
|
|
|
|
/**
|
|
* Parses the XPointer framework expression and delegates scheme specific parsing.
|
|
*
|
|
* @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
|
|
*/
|
|
public void parseXPointer(String xpointer) throws XNIException {
|
|
|
|
// Initialize
|
|
init();
|
|
|
|
// tokens
|
|
final Tokens tokens = new Tokens(fSymbolTable);
|
|
|
|
// scanner
|
|
Scanner scanner = new Scanner(fSymbolTable) {
|
|
protected void addToken(Tokens tokens, int token)
|
|
throws XNIException {
|
|
if (token == Tokens.XPTRTOKEN_OPEN_PAREN
|
|
|| token == Tokens.XPTRTOKEN_CLOSE_PAREN
|
|
|| token == Tokens.XPTRTOKEN_SCHEMENAME
|
|
|| token == Tokens.XPTRTOKEN_SCHEMEDATA
|
|
|| token == Tokens.XPTRTOKEN_SHORTHAND) {
|
|
super.addToken(tokens, token);
|
|
return;
|
|
}
|
|
reportError("InvalidXPointerToken", new Object[] { tokens
|
|
.getTokenString(token) });
|
|
}
|
|
};
|
|
|
|
// scan the XPointer expression
|
|
int length = xpointer.length();
|
|
boolean success = scanner.scanExpr(fSymbolTable, tokens, xpointer, 0,
|
|
length);
|
|
|
|
if (!success)
|
|
reportError("InvalidXPointerExpression", new Object[] { xpointer });
|
|
|
|
while (tokens.hasMore()) {
|
|
int token = tokens.nextToken();
|
|
|
|
switch (token) {
|
|
case Tokens.XPTRTOKEN_SHORTHAND: {
|
|
|
|
// The shortHand name
|
|
token = tokens.nextToken();
|
|
String shortHandPointerName = tokens.getTokenString(token);
|
|
|
|
if (shortHandPointerName == null) {
|
|
reportError("InvalidXPointerExpression",
|
|
new Object[] { xpointer });
|
|
}
|
|
|
|
XPointerPart shortHandPointer = new ShortHandPointer(
|
|
fSymbolTable);
|
|
shortHandPointer.setSchemeName(shortHandPointerName);
|
|
fXPointerParts.add(shortHandPointer);
|
|
break;
|
|
}
|
|
case Tokens.XPTRTOKEN_SCHEMENAME: {
|
|
|
|
// Retreive the local name and prefix to form the scheme name
|
|
token = tokens.nextToken();
|
|
String prefix = tokens.getTokenString(token);
|
|
token = tokens.nextToken();
|
|
String localName = tokens.getTokenString(token);
|
|
|
|
String schemeName = prefix + localName;
|
|
|
|
// The next character should be an open parenthesis
|
|
int openParenCount = 0;
|
|
int closeParenCount = 0;
|
|
|
|
token = tokens.nextToken();
|
|
String openParen = tokens.getTokenString(token);
|
|
if (openParen != "XPTRTOKEN_OPEN_PAREN") {
|
|
|
|
// can not have more than one ShortHand Pointer
|
|
if (token == Tokens.XPTRTOKEN_SHORTHAND) {
|
|
reportError("MultipleShortHandPointers",
|
|
new Object[] { xpointer });
|
|
} else {
|
|
reportError("InvalidXPointerExpression",
|
|
new Object[] { xpointer });
|
|
}
|
|
}
|
|
openParenCount++;
|
|
|
|
// followed by zero or more ( and the schemeData
|
|
String schemeData = null;
|
|
while (tokens.hasMore()) {
|
|
token = tokens.nextToken();
|
|
schemeData = tokens.getTokenString(token);
|
|
if (schemeData != "XPTRTOKEN_OPEN_PAREN") {
|
|
break;
|
|
}
|
|
openParenCount++;
|
|
}
|
|
token = tokens.nextToken();
|
|
schemeData = tokens.getTokenString(token);
|
|
|
|
// followed by the same number of )
|
|
token = tokens.nextToken();
|
|
String closeParen = tokens.getTokenString(token);
|
|
if (closeParen != "XPTRTOKEN_CLOSE_PAREN") {
|
|
reportError("SchemeDataNotFollowedByCloseParenthesis",
|
|
new Object[] { xpointer });
|
|
}
|
|
closeParenCount++;
|
|
|
|
while (tokens.hasMore()) {
|
|
if (tokens.getTokenString(tokens.peekToken()) != "XPTRTOKEN_OPEN_PAREN") {
|
|
break;
|
|
}
|
|
closeParenCount++;
|
|
}
|
|
|
|
// check if the number of open parenthesis are equal to the number of close parenthesis
|
|
if (openParenCount != closeParenCount) {
|
|
reportError("UnbalancedParenthesisInXPointerExpression",
|
|
new Object[] { xpointer,
|
|
new Integer(openParenCount),
|
|
new Integer(closeParenCount) });
|
|
}
|
|
|
|
// Perform scheme specific parsing of the pointer part
|
|
if (schemeName.equals(ELEMENT_SCHEME_NAME)) {
|
|
XPointerPart elementSchemePointer = new ElementSchemePointer(
|
|
fSymbolTable, fErrorReporter);
|
|
elementSchemePointer.setSchemeName(schemeName);
|
|
elementSchemePointer.setSchemeData(schemeData);
|
|
|
|
// If an exception occurs while parsing the element() scheme expression
|
|
// ignore it and move on to the next pointer part
|
|
try {
|
|
elementSchemePointer.parseXPointer(schemeData);
|
|
fXPointerParts.add(elementSchemePointer);
|
|
} catch (XNIException e) {
|
|
// Re-throw the XPointer element() scheme syntax error.
|
|
throw new XNIException (e);
|
|
}
|
|
|
|
} else {
|
|
// ????
|
|
reportWarning("SchemeUnsupported",
|
|
new Object[] { schemeName });
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
reportError("InvalidXPointerExpression",
|
|
new Object[] { xpointer });
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#resolveXPointer(com.sun.org.apache.xerces.internal.xni.QName, com.sun.org.apache.xerces.internal.xni.XMLAttributes, com.sun.org.apache.xerces.internal.xni.Augmentations, int event)
|
|
*/
|
|
public boolean resolveXPointer(QName element, XMLAttributes attributes,
|
|
Augmentations augs, int event) throws XNIException {
|
|
boolean resolved = false;
|
|
|
|
// The result of the first pointer part whose evaluation identifies
|
|
// one or more subresources is reported by the XPointer processor as the
|
|
// result of the pointer as a whole, and evaluation stops.
|
|
// In our implementation, typically the first xpointer scheme that
|
|
// matches an element is the document is considered.
|
|
// If the pointer part resolved then use it, else search for the fragment
|
|
// using next pointer part from lef-right.
|
|
if (!fFoundMatchingPtrPart) {
|
|
|
|
// for each element, attempt to resolve it against each pointer part
|
|
// in the XPointer expression until a matching element is found.
|
|
for (int i = 0; i < fXPointerParts.size(); i++) {
|
|
|
|
fXPointerPart = (XPointerPart) fXPointerParts.get(i);
|
|
|
|
if (fXPointerPart.resolveXPointer(element, attributes, augs,
|
|
event)) {
|
|
fFoundMatchingPtrPart = true;
|
|
resolved = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (fXPointerPart.resolveXPointer(element, attributes, augs, event)) {
|
|
resolved = true;
|
|
}
|
|
}
|
|
|
|
if (!fIsXPointerResolved) {
|
|
fIsXPointerResolved = resolved;
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the Node fragment is resolved.
|
|
*
|
|
* @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
|
|
*/
|
|
public boolean isFragmentResolved() throws XNIException {
|
|
boolean resolved = (fXPointerPart != null) ? fXPointerPart.isFragmentResolved()
|
|
: false;
|
|
|
|
if (!fIsXPointerResolved) {
|
|
fIsXPointerResolved = resolved;
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the XPointer expression resolves to a non-element child
|
|
* of the current resource fragment.
|
|
*
|
|
* @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved()
|
|
*
|
|
*/
|
|
public boolean isChildFragmentResolved() throws XNIException {
|
|
boolean resolved = (fXPointerPart != null) ? fXPointerPart
|
|
.isChildFragmentResolved() : false;
|
|
return resolved;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the XPointer successfully found a sub-resource .
|
|
*
|
|
* @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
|
|
*/
|
|
public boolean isXPointerResolved() throws XNIException {
|
|
return fIsXPointerResolved;
|
|
}
|
|
|
|
/**
|
|
* Returns the pointer part used to resolve the document fragment.
|
|
*
|
|
* @return String - The pointer part used to resolve the document fragment.
|
|
*/
|
|
public XPointerPart getXPointerPart() {
|
|
return fXPointerPart;
|
|
}
|
|
|
|
/**
|
|
* Reports XPointer Errors
|
|
*
|
|
*/
|
|
private void reportError(String key, Object[] arguments)
|
|
throws XNIException {
|
|
/*
|
|
fXPointerErrorReporter.reportError(
|
|
XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
|
|
XMLErrorReporter.SEVERITY_ERROR);
|
|
*/
|
|
throw new XNIException((fErrorReporter
|
|
.getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
|
|
.formatMessage(fErrorReporter.getLocale(), key, arguments));
|
|
}
|
|
|
|
/**
|
|
* Reports XPointer Warnings
|
|
*
|
|
*/
|
|
private void reportWarning(String key, Object[] arguments)
|
|
throws XNIException {
|
|
fXPointerErrorReporter.reportError(
|
|
XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
|
|
XMLErrorReporter.SEVERITY_WARNING);
|
|
}
|
|
|
|
/**
|
|
* Initializes error handling objects
|
|
*
|
|
*/
|
|
protected void initErrorReporter() {
|
|
if (fXPointerErrorReporter == null) {
|
|
fXPointerErrorReporter = new XMLErrorReporter();
|
|
}
|
|
if (fErrorHandler == null) {
|
|
fErrorHandler = new XPointerErrorHandler();
|
|
}
|
|
/*
|
|
fXPointerErrorReporter.setProperty(Constants.XERCES_PROPERTY_PREFIX
|
|
+ Constants.ERROR_HANDLER_PROPERTY, fErrorHandler);
|
|
*/
|
|
fXPointerErrorReporter.putMessageFormatter(
|
|
XPointerMessageFormatter.XPOINTER_DOMAIN,
|
|
new XPointerMessageFormatter());
|
|
}
|
|
|
|
/**
|
|
* Initializes the XPointer Processor;
|
|
*/
|
|
protected void init() {
|
|
fXPointerParts.clear();
|
|
fXPointerPart = null;
|
|
fFoundMatchingPtrPart = false;
|
|
fIsXPointerResolved = false;
|
|
//fFixupBase = false;
|
|
//fFixupLang = false;
|
|
|
|
initErrorReporter();
|
|
}
|
|
|
|
/**
|
|
* Returns a Vector of XPointerPart objects
|
|
*
|
|
* @return A Vector of XPointerPart objects.
|
|
*/
|
|
public Vector getPointerParts() {
|
|
return fXPointerParts;
|
|
}
|
|
|
|
/**
|
|
* List of XPointer Framework tokens.
|
|
*
|
|
* @xerces.internal
|
|
*
|
|
*/
|
|
private final class Tokens {
|
|
|
|
/**
|
|
* XPointer Framework tokens
|
|
* [1] Pointer ::= Shorthand | SchemeBased
|
|
* [2] Shorthand ::= NCName
|
|
* [3] SchemeBased ::= PointerPart (S? PointerPart)*
|
|
* [4] PointerPart ::= SchemeName '(' SchemeData ')'
|
|
* [5] SchemeName ::= QName
|
|
* [6] SchemeData ::= EscapedData*
|
|
* [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
|
|
* [8] NormalChar ::= UnicodeChar - [()^]
|
|
* [9] UnicodeChar ::= [#x0-#x10FFFF]
|
|
*
|
|
*/
|
|
private static final int XPTRTOKEN_OPEN_PAREN = 0,
|
|
XPTRTOKEN_CLOSE_PAREN = 1, XPTRTOKEN_SHORTHAND = 2,
|
|
XPTRTOKEN_SCHEMENAME = 3, XPTRTOKEN_SCHEMEDATA = 4;
|
|
|
|
// Token names
|
|
private final String[] fgTokenNames = { "XPTRTOKEN_OPEN_PAREN",
|
|
"XPTRTOKEN_CLOSE_PAREN", "XPTRTOKEN_SHORTHAND",
|
|
"XPTRTOKEN_SCHEMENAME", "XPTRTOKEN_SCHEMEDATA" };
|
|
|
|
// Token count
|
|
private static final int INITIAL_TOKEN_COUNT = 1 << 8;
|
|
|
|
private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
|
|
|
|
private int fTokenCount = 0;
|
|
|
|
// Current token position
|
|
private int fCurrentTokenIndex;
|
|
|
|
private SymbolTable fSymbolTable;
|
|
|
|
private Hashtable fTokenNames = new Hashtable();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param symbolTable SymbolTable
|
|
*/
|
|
private Tokens(SymbolTable symbolTable) {
|
|
fSymbolTable = symbolTable;
|
|
|
|
fTokenNames.put(new Integer(XPTRTOKEN_OPEN_PAREN),
|
|
"XPTRTOKEN_OPEN_PAREN");
|
|
fTokenNames.put(new Integer(XPTRTOKEN_CLOSE_PAREN),
|
|
"XPTRTOKEN_CLOSE_PAREN");
|
|
fTokenNames.put(new Integer(XPTRTOKEN_SHORTHAND),
|
|
"XPTRTOKEN_SHORTHAND");
|
|
fTokenNames.put(new Integer(XPTRTOKEN_SCHEMENAME),
|
|
"XPTRTOKEN_SCHEMENAME");
|
|
fTokenNames.put(new Integer(XPTRTOKEN_SCHEMEDATA),
|
|
"XPTRTOKEN_SCHEMEDATA");
|
|
}
|
|
|
|
/**
|
|
* Returns the token String
|
|
* @param token The index of the token
|
|
* @return String The token string
|
|
*/
|
|
private String getTokenString(int token) {
|
|
return (String) fTokenNames.get(new Integer(token));
|
|
}
|
|
|
|
/**
|
|
* Add the specified string as a token
|
|
*
|
|
* @param token The token string
|
|
*/
|
|
private void addToken(String tokenStr) {
|
|
Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
|
|
if (tokenInt == null) {
|
|
tokenInt = new Integer(fTokenNames.size());
|
|
fTokenNames.put(tokenInt, tokenStr);
|
|
}
|
|
addToken(tokenInt.intValue());
|
|
}
|
|
|
|
/**
|
|
* Add the specified int token
|
|
*
|
|
* @param token The int specifying the token
|
|
*/
|
|
private void addToken(int token) {
|
|
try {
|
|
fTokens[fTokenCount] = token;
|
|
} catch (ArrayIndexOutOfBoundsException ex) {
|
|
int[] oldList = fTokens;
|
|
fTokens = new int[fTokenCount << 1];
|
|
System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
|
|
fTokens[fTokenCount] = token;
|
|
}
|
|
fTokenCount++;
|
|
}
|
|
|
|
/**
|
|
* Resets the current position to the head of the token list.
|
|
*/
|
|
private void rewind() {
|
|
fCurrentTokenIndex = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the {@link #getNextToken()} method
|
|
* returns a valid token.
|
|
*/
|
|
private boolean hasMore() {
|
|
return fCurrentTokenIndex < fTokenCount;
|
|
}
|
|
|
|
/**
|
|
* Obtains the token at the current position, then advance
|
|
* the current position by one.
|
|
*
|
|
* throws If there's no such next token, this method throws
|
|
* <tt>new XNIException("XPointerProcessingError");</tt>.
|
|
*/
|
|
private int nextToken() throws XNIException {
|
|
if (fCurrentTokenIndex == fTokenCount) {
|
|
reportError("XPointerProcessingError", null);
|
|
}
|
|
return fTokens[fCurrentTokenIndex++];
|
|
}
|
|
|
|
/**
|
|
* Obtains the token at the current position, without advancing
|
|
* the current position.
|
|
*
|
|
* If there's no such next token, this method throws
|
|
* <tt>new XNIException("XPointerProcessingError");</tt>.
|
|
*/
|
|
private int peekToken() throws XNIException {
|
|
if (fCurrentTokenIndex == fTokenCount) {
|
|
reportError("XPointerProcessingError", null);
|
|
}
|
|
return fTokens[fCurrentTokenIndex];
|
|
}
|
|
|
|
/**
|
|
* Obtains the token at the current position as a String.
|
|
*
|
|
* If there's no current token or if the current token
|
|
* is not a string token, this method throws
|
|
* If there's no such next token, this method throws
|
|
* <tt>new XNIException("XPointerProcessingError");</tt>.
|
|
*/
|
|
private String nextTokenAsString() throws XNIException {
|
|
String tokenStrint = getTokenString(nextToken());
|
|
if (tokenStrint == null) {
|
|
reportError("XPointerProcessingError", null);
|
|
}
|
|
return tokenStrint;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* The XPointer expression scanner. Scans the XPointer framework expression.
|
|
*
|
|
* @xerces.internal
|
|
*
|
|
*/
|
|
private class Scanner {
|
|
|
|
/**
|
|
* 7-bit ASCII subset
|
|
*
|
|
* 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
|
|
* SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
|
|
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
|
|
* @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
|
|
* P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
|
|
* `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
|
|
* p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
|
|
*/
|
|
private static final byte CHARTYPE_INVALID = 0, // invalid XML character
|
|
CHARTYPE_OTHER = 1, // not special - one of "#%&;?\`{}~" or DEL
|
|
CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
|
|
CHARTYPE_CARRET = 3, // ^
|
|
CHARTYPE_OPEN_PAREN = 4, // '(' (0x28)
|
|
CHARTYPE_CLOSE_PAREN = 5, // ')' (0x29)
|
|
CHARTYPE_MINUS = 6, // '-' (0x2D)
|
|
CHARTYPE_PERIOD = 7, // '.' (0x2E)
|
|
CHARTYPE_SLASH = 8, // '/' (0x2F)
|
|
CHARTYPE_DIGIT = 9, // '0'-'9' (0x30 to 0x39)
|
|
CHARTYPE_COLON = 10, // ':' (0x3A)
|
|
CHARTYPE_EQUAL = 11, // '=' (0x3D)
|
|
CHARTYPE_LETTER = 12, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
|
|
CHARTYPE_UNDERSCORE = 13, // '_' (0x5F)
|
|
CHARTYPE_NONASCII = 14; // Non-ASCII Unicode codepoint (>= 0x80)
|
|
|
|
private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
|
|
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 6, 7, 8, 9, 9, 9, 9, 9,
|
|
9, 9, 9, 9, 9, 10, 1, 1, 11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
|
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
|
12, 12, 12, 12, 1, 1, 1, 3, 13, 1, 12, 12, 12, 12, 12, 12, 12,
|
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
|
12, 12, 12, 1, 1, 1, 1, 1 };
|
|
|
|
//
|
|
// Data
|
|
//
|
|
/** Symbol table. */
|
|
private SymbolTable fSymbolTable;
|
|
|
|
/**
|
|
* Constructs an XPointer Framework expression scanner.
|
|
*
|
|
* @param symbolTable SymbolTable
|
|
*/
|
|
private Scanner(SymbolTable symbolTable) {
|
|
// save pool and tokens
|
|
fSymbolTable = symbolTable;
|
|
|
|
} // <init>(SymbolTable)
|
|
|
|
/**
|
|
* Scans the XPointer Expression
|
|
*
|
|
*/
|
|
private boolean scanExpr(SymbolTable symbolTable, Tokens tokens,
|
|
String data, int currentOffset, int endOffset)
|
|
throws XNIException {
|
|
|
|
int ch;
|
|
int openParen = 0;
|
|
int closeParen = 0;
|
|
int nameOffset, dataOffset;
|
|
boolean isQName = false;
|
|
String name = null;
|
|
String prefix = null;
|
|
String schemeData = null;
|
|
StringBuffer schemeDataBuff = new StringBuffer();
|
|
|
|
while (true) {
|
|
|
|
if (currentOffset == endOffset) {
|
|
break;
|
|
}
|
|
ch = data.charAt(currentOffset);
|
|
|
|
//
|
|
while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
|
|
if (++currentOffset == endOffset) {
|
|
break;
|
|
}
|
|
ch = data.charAt(currentOffset);
|
|
}
|
|
if (currentOffset == endOffset) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// [1] Pointer ::= Shorthand | SchemeBased
|
|
// [2] Shorthand ::= NCName
|
|
// [3] SchemeBased ::= PointerPart (S? PointerPart)*
|
|
// [4] PointerPart ::= SchemeName '(' SchemeData ')'
|
|
// [5] SchemeName ::= QName
|
|
// [6] SchemeData ::= EscapedData*
|
|
// [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
|
|
// [8] NormalChar ::= UnicodeChar - [()^]
|
|
// [9] UnicodeChar ::= [#x0-#x10FFFF]
|
|
// [?] QName ::= (NCName ':')? NCName
|
|
// [?] NCName ::= (Letter | '_') (NCNameChar)*
|
|
// [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
|
|
// [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
|
|
// [?] Digit ::= [0-9] (ascii subset of 'Digit')
|
|
//
|
|
byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
|
|
: fASCIICharMap[ch];
|
|
|
|
switch (chartype) {
|
|
|
|
case CHARTYPE_OPEN_PAREN: // '('
|
|
addToken(tokens, Tokens.XPTRTOKEN_OPEN_PAREN);
|
|
openParen++;
|
|
++currentOffset;
|
|
break;
|
|
|
|
case CHARTYPE_CLOSE_PAREN: // ')'
|
|
addToken(tokens, Tokens.XPTRTOKEN_CLOSE_PAREN);
|
|
closeParen++;
|
|
++currentOffset;
|
|
break;
|
|
|
|
case CHARTYPE_CARRET:
|
|
case CHARTYPE_COLON:
|
|
case CHARTYPE_DIGIT:
|
|
case CHARTYPE_EQUAL:
|
|
case CHARTYPE_LETTER:
|
|
case CHARTYPE_MINUS:
|
|
case CHARTYPE_NONASCII:
|
|
case CHARTYPE_OTHER:
|
|
case CHARTYPE_PERIOD:
|
|
case CHARTYPE_SLASH:
|
|
case CHARTYPE_UNDERSCORE:
|
|
case CHARTYPE_WHITESPACE:
|
|
// Scanning SchemeName | Shorthand
|
|
if (openParen == 0) {
|
|
nameOffset = currentOffset;
|
|
currentOffset = scanNCName(data, endOffset,
|
|
currentOffset);
|
|
|
|
if (currentOffset == nameOffset) {
|
|
reportError("InvalidShortHandPointer",
|
|
new Object[] { data });
|
|
return false;
|
|
}
|
|
|
|
if (currentOffset < endOffset) {
|
|
ch = data.charAt(currentOffset);
|
|
} else {
|
|
ch = -1;
|
|
}
|
|
|
|
name = symbolTable.addSymbol(data.substring(nameOffset,
|
|
currentOffset));
|
|
prefix = XMLSymbols.EMPTY_STRING;
|
|
|
|
// The name is a QName => a SchemeName
|
|
if (ch == ':') {
|
|
if (++currentOffset == endOffset) {
|
|
return false;
|
|
}
|
|
|
|
ch = data.charAt(currentOffset);
|
|
prefix = name;
|
|
nameOffset = currentOffset;
|
|
currentOffset = scanNCName(data, endOffset,
|
|
currentOffset);
|
|
|
|
if (currentOffset == nameOffset) {
|
|
return false;
|
|
}
|
|
|
|
if (currentOffset < endOffset) {
|
|
ch = data.charAt(currentOffset);
|
|
} else {
|
|
ch = -1;
|
|
}
|
|
|
|
isQName = true;
|
|
name = symbolTable.addSymbol(data.substring(
|
|
nameOffset, currentOffset));
|
|
}
|
|
|
|
// REVISIT:
|
|
if (currentOffset != endOffset) {
|
|
addToken(tokens, Tokens.XPTRTOKEN_SCHEMENAME);
|
|
tokens.addToken(prefix);
|
|
tokens.addToken(name);
|
|
isQName = false;
|
|
} else if (currentOffset == endOffset) {
|
|
// NCName => Shorthand
|
|
addToken(tokens, Tokens.XPTRTOKEN_SHORTHAND);
|
|
tokens.addToken(name);
|
|
isQName = false;
|
|
}
|
|
|
|
// reset open/close paren for the next pointer part
|
|
closeParen = 0;
|
|
|
|
break;
|
|
|
|
} else if (openParen > 0 && closeParen == 0 && name != null) {
|
|
// Scanning SchemeData
|
|
dataOffset = currentOffset;
|
|
currentOffset = scanData(data, schemeDataBuff,
|
|
endOffset, currentOffset);
|
|
|
|
if (currentOffset == dataOffset) {
|
|
reportError("InvalidSchemeDataInXPointer",
|
|
new Object[] { data });
|
|
return false;
|
|
}
|
|
|
|
if (currentOffset < endOffset) {
|
|
ch = data.charAt(currentOffset);
|
|
} else {
|
|
ch = -1;
|
|
}
|
|
|
|
schemeData = symbolTable.addSymbol(schemeDataBuff
|
|
.toString());
|
|
addToken(tokens, Tokens.XPTRTOKEN_SCHEMEDATA);
|
|
tokens.addToken(schemeData);
|
|
|
|
// reset open/close paren for the next pointer part
|
|
openParen = 0;
|
|
schemeDataBuff.delete(0, schemeDataBuff.length());
|
|
|
|
} else {
|
|
// ex. schemeName()
|
|
// Should we throw an exception with a more suitable message instead??
|
|
return false;
|
|
}
|
|
}
|
|
} // end while
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Scans a NCName.
|
|
* From Namespaces in XML
|
|
* [5] NCName ::= (Letter | '_') (NCNameChar)*
|
|
* [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
|
|
*
|
|
* @param data A String containing the XPointer expression
|
|
* @param endOffset The int XPointer expression length
|
|
* @param currentOffset An int representing the current position of the XPointer expression pointer
|
|
*/
|
|
private int scanNCName(String data, int endOffset, int currentOffset) {
|
|
int ch = data.charAt(currentOffset);
|
|
if (ch >= 0x80) {
|
|
if (!XMLChar.isNameStart(ch)) {
|
|
return currentOffset;
|
|
}
|
|
} else {
|
|
byte chartype = fASCIICharMap[ch];
|
|
if (chartype != CHARTYPE_LETTER
|
|
&& chartype != CHARTYPE_UNDERSCORE) {
|
|
return currentOffset;
|
|
}
|
|
}
|
|
|
|
//while (currentOffset++ < endOffset) {
|
|
while (++currentOffset < endOffset) {
|
|
ch = data.charAt(currentOffset);
|
|
if (ch >= 0x80) {
|
|
if (!XMLChar.isName(ch)) {
|
|
break;
|
|
}
|
|
} else {
|
|
byte chartype = fASCIICharMap[ch];
|
|
if (chartype != CHARTYPE_LETTER
|
|
&& chartype != CHARTYPE_DIGIT
|
|
&& chartype != CHARTYPE_PERIOD
|
|
&& chartype != CHARTYPE_MINUS
|
|
&& chartype != CHARTYPE_UNDERSCORE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return currentOffset;
|
|
}
|
|
|
|
/**
|
|
* Scans the SchemeData.
|
|
* [6] SchemeData ::= EscapedData*
|
|
* [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
|
|
* [8] NormalChar ::= UnicodeChar - [()^]
|
|
* [9] UnicodeChar ::= [#x0-#x10FFFF]
|
|
*
|
|
*/
|
|
private int scanData(String data, StringBuffer schemeData,
|
|
int endOffset, int currentOffset) {
|
|
while (true) {
|
|
|
|
if (currentOffset == endOffset) {
|
|
break;
|
|
}
|
|
|
|
int ch = data.charAt(currentOffset);
|
|
byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
|
|
: fASCIICharMap[ch];
|
|
|
|
if (chartype == CHARTYPE_OPEN_PAREN) {
|
|
schemeData.append(ch);
|
|
//schemeData.append(Tokens.XPTRTOKEN_OPEN_PAREN);
|
|
currentOffset = scanData(data, schemeData, endOffset,
|
|
++currentOffset);
|
|
if (currentOffset == endOffset) {
|
|
return currentOffset;
|
|
}
|
|
|
|
ch = data.charAt(currentOffset);
|
|
chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
|
|
: fASCIICharMap[ch];
|
|
|
|
if (chartype != CHARTYPE_CLOSE_PAREN) {
|
|
return endOffset;
|
|
}
|
|
schemeData.append((char) ch);
|
|
++currentOffset;//
|
|
|
|
} else if (chartype == CHARTYPE_CLOSE_PAREN) {
|
|
return currentOffset;
|
|
|
|
} else if (chartype == CHARTYPE_CARRET) {
|
|
ch = data.charAt(++currentOffset);
|
|
chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
|
|
: fASCIICharMap[ch];
|
|
|
|
if (chartype != CHARTYPE_CARRET
|
|
&& chartype != CHARTYPE_OPEN_PAREN
|
|
&& chartype != CHARTYPE_CLOSE_PAREN) {
|
|
break;
|
|
}
|
|
schemeData.append((char) ch);
|
|
++currentOffset;
|
|
|
|
} else {
|
|
schemeData.append((char) ch);
|
|
++currentOffset;//
|
|
}
|
|
}
|
|
|
|
return currentOffset;
|
|
}
|
|
|
|
//
|
|
// Protected methods
|
|
//
|
|
|
|
/**
|
|
* This method adds the specified token to the token list. By
|
|
* default, this method allows all tokens. However, subclasses
|
|
* of the XPathExprScanner can override this method in order
|
|
* to disallow certain tokens from being used in the scanned
|
|
* XPath expression. This is a convenient way of allowing only
|
|
* a subset of XPath.
|
|
*/
|
|
protected void addToken(Tokens tokens, int token) throws XNIException {
|
|
tokens.addToken(token);
|
|
} // addToken(int)
|
|
|
|
} // class Scanner
|
|
|
|
// ************************************************************************
|
|
// Overridden XMLDocumentHandler methods
|
|
// ************************************************************************
|
|
/**
|
|
* If the comment is a child of a matched element, then pass else return.
|
|
*
|
|
* @param text The text in the comment.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by application to signal an error.
|
|
*/
|
|
public void comment(XMLString text, Augmentations augs) throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.comment(text, augs);
|
|
}
|
|
|
|
/**
|
|
* A processing instruction. Processing instructions consist of a
|
|
* target name and, optionally, text data. The data is only meaningful
|
|
* to the application.
|
|
* <p>
|
|
* Typically, a processing instruction's data will contain a series
|
|
* of pseudo-attributes. These pseudo-attributes follow the form of
|
|
* element attributes but are <strong>not</strong> parsed or presented
|
|
* to the application as anything other than text. The application is
|
|
* responsible for parsing the data.
|
|
*
|
|
* @param target The target.
|
|
* @param data The data or null if none specified.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void processingInstruction(String target, XMLString data,
|
|
Augmentations augs) throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.processingInstruction(target, data, augs);
|
|
}
|
|
|
|
/**
|
|
* The start of an element.
|
|
*
|
|
* @param element The name of the element.
|
|
* @param attributes The element attributes.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void startElement(QName element, XMLAttributes attributes,
|
|
Augmentations augs) throws XNIException {
|
|
if (!resolveXPointer(element, attributes, augs,
|
|
XPointerPart.EVENT_ELEMENT_START)) {
|
|
|
|
// xml:base and xml:lang processing
|
|
if (fFixupBase) {
|
|
processXMLBaseAttributes(attributes);
|
|
}
|
|
if (fFixupLang) {
|
|
processXMLLangAttributes(attributes);
|
|
}
|
|
|
|
// set the context invalid if the element till an element from the result infoset is included
|
|
fNamespaceContext.setContextInvalid();
|
|
|
|
return;
|
|
}
|
|
super.startElement(element, attributes, augs);
|
|
}
|
|
|
|
/**
|
|
* An empty element.
|
|
*
|
|
* @param element The name of the element.
|
|
* @param attributes The element attributes.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void emptyElement(QName element, XMLAttributes attributes,
|
|
Augmentations augs) throws XNIException {
|
|
if (!resolveXPointer(element, attributes, augs,
|
|
XPointerPart.EVENT_ELEMENT_EMPTY)) {
|
|
// xml:base and xml:lang processing
|
|
if (fFixupBase) {
|
|
processXMLBaseAttributes(attributes);
|
|
}
|
|
if (fFixupLang) {
|
|
processXMLLangAttributes(attributes);
|
|
}
|
|
// no need to restore restoreBaseURI() for xml:base and xml:lang processing
|
|
|
|
// set the context invalid if the element till an element from the result infoset is included
|
|
fNamespaceContext.setContextInvalid();
|
|
return;
|
|
}
|
|
super.emptyElement(element, attributes, augs);
|
|
}
|
|
|
|
/**
|
|
* Character content.
|
|
*
|
|
* @param text The content.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void characters(XMLString text, Augmentations augs)
|
|
throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.characters(text, augs);
|
|
}
|
|
|
|
/**
|
|
* Ignorable whitespace. For this method to be called, the document
|
|
* source must have some way of determining that the text containing
|
|
* only whitespace characters should be considered ignorable. For
|
|
* example, the validator can determine if a length of whitespace
|
|
* characters in the document are ignorable based on the element
|
|
* content model.
|
|
*
|
|
* @param text The ignorable whitespace.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void ignorableWhitespace(XMLString text, Augmentations augs)
|
|
throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.ignorableWhitespace(text, augs);
|
|
}
|
|
|
|
/**
|
|
* The end of an element.
|
|
*
|
|
* @param element The name of the element.
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void endElement(QName element, Augmentations augs)
|
|
throws XNIException {
|
|
if (!resolveXPointer(element, null, augs,
|
|
XPointerPart.EVENT_ELEMENT_END)) {
|
|
|
|
// no need to restore restoreBaseURI() for xml:base and xml:lang processing
|
|
return;
|
|
}
|
|
super.endElement(element, augs);
|
|
}
|
|
|
|
/**
|
|
* The start of a CDATA section.
|
|
*
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void startCDATA(Augmentations augs) throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.startCDATA(augs);
|
|
}
|
|
|
|
/**
|
|
* The end of a CDATA section.
|
|
*
|
|
* @param augs Additional information that may include infoset augmentations
|
|
*
|
|
* @exception XNIException
|
|
* Thrown by handler to signal an error.
|
|
*/
|
|
public void endCDATA(Augmentations augs) throws XNIException {
|
|
if (!isChildFragmentResolved()) {
|
|
return;
|
|
}
|
|
super.endCDATA(augs);
|
|
}
|
|
|
|
// ************************************************************************
|
|
// Overridden XMLComponent methods
|
|
// ************************************************************************
|
|
/**
|
|
* <p>
|
|
* Sets the value of a property. This method is called by the component
|
|
* manager any time after reset when a property changes value.
|
|
* </p>
|
|
* <strong>Note:</strong> Components should silently ignore properties
|
|
* that do not affect the operation of the component.
|
|
*
|
|
* @param propertyId The property identifier.
|
|
* @param value The value of the property.
|
|
*
|
|
* @throws XMLConfigurationException Thrown for configuration error.
|
|
* In general, components should
|
|
* only throw this exception if
|
|
* it is <strong>really</strong>
|
|
* a critical error.
|
|
*/
|
|
public void setProperty(String propertyId, Object value)
|
|
throws XMLConfigurationException {
|
|
|
|
// Error reporter
|
|
if (propertyId == Constants.XERCES_PROPERTY_PREFIX
|
|
+ Constants.ERROR_REPORTER_PROPERTY) {
|
|
if (value != null) {
|
|
fXPointerErrorReporter = (XMLErrorReporter) value;
|
|
} else {
|
|
fXPointerErrorReporter = null;
|
|
}
|
|
}
|
|
|
|
// Error handler
|
|
if (propertyId == Constants.XERCES_PROPERTY_PREFIX
|
|
+ Constants.ERROR_HANDLER_PROPERTY) {
|
|
if (value != null) {
|
|
fErrorHandler = (XMLErrorHandler) value;
|
|
} else {
|
|
fErrorHandler = null;
|
|
}
|
|
}
|
|
|
|
// xml:lang
|
|
if (propertyId == Constants.XERCES_FEATURE_PREFIX
|
|
+ Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE) {
|
|
if (value != null) {
|
|
fFixupLang = ((Boolean)value).booleanValue();
|
|
} else {
|
|
fFixupLang = false;
|
|
}
|
|
}
|
|
|
|
// xml:base
|
|
if (propertyId == Constants.XERCES_FEATURE_PREFIX
|
|
+ Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE) {
|
|
if (value != null) {
|
|
fFixupBase = ((Boolean)value).booleanValue();
|
|
} else {
|
|
fFixupBase = false;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (propertyId == Constants.XERCES_PROPERTY_PREFIX
|
|
+ Constants.NAMESPACE_CONTEXT_PROPERTY) {
|
|
fNamespaceContext = (XIncludeNamespaceSupport) value;
|
|
}
|
|
|
|
super.setProperty(propertyId, value);
|
|
}
|
|
|
|
}
|