blob: 30b75630b547e3e6ea8b5035d915fe4da2b6979d [file] [log] [blame]
ohair6c320662012-03-04 11:55:34 -08001/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Copyright 2005 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package com.sun.org.apache.xerces.internal.xpointer;
21
22import java.util.Hashtable;
23import java.util.Vector;
24
25import com.sun.org.apache.xerces.internal.impl.Constants;
26import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
27import com.sun.org.apache.xerces.internal.util.SymbolTable;
28import com.sun.org.apache.xerces.internal.util.XMLChar;
29import com.sun.org.apache.xerces.internal.util.XMLSymbols;
30import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
31import com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport;
32import com.sun.org.apache.xerces.internal.xni.Augmentations;
33import com.sun.org.apache.xerces.internal.xni.QName;
34import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
35import com.sun.org.apache.xerces.internal.xni.XMLString;
36import com.sun.org.apache.xerces.internal.xni.XNIException;
37import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
38import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
39
40/**
41 * <p>
42 * This is a pipeline component which extends the XIncludeHandler to perform
43 * XPointer specific processing specified in the W3C XPointerFramework and
44 * element() Scheme Recommendations.
45 * </p>
46 *
47 * <p>
48 * This component analyzes each event in the pipeline, looking for an element
49 * that matches a PointerPart in the parent XInclude element's xpointer attribute
50 * value. If the match succeeds, all children are passed by this component.
51 * </p>
52 *
53 * <p>
54 * See the <a href="http://www.w3.org/TR/xptr-framework//">XPointer Framework Recommendation</a> for
55 * more information on the XPointer Framework and ShortHand Pointers.
56 * See the <a href="http://www.w3.org/TR/xptr-element/">XPointer element() Scheme Recommendation</a> for
57 * more information on the XPointer element() Scheme.
58 * </p>
59 *
60 * @xerces.internal
61 *
62 */
63public final class XPointerHandler extends XIncludeHandler implements
64 XPointerProcessor {
65
66 // Fields
67 // A Vector of XPointerParts
68 protected Vector fXPointerParts = null;
69
70 // The current XPointerPart
71 protected XPointerPart fXPointerPart = null;
72
73 // Has the fXPointerPart resolved successfully
74 protected boolean fFoundMatchingPtrPart = false;
75
76 // The XPointer Error reporter
77 protected XMLErrorReporter fXPointerErrorReporter;
78
79 // The XPointer Error Handler
80 protected XMLErrorHandler fErrorHandler;
81
82 // XPointerFramework symbol table
83 protected SymbolTable fSymbolTable = null;
84
85 // Supported schemes
86 private final String ELEMENT_SCHEME_NAME = "element";
87
88 // Has the XPointer resolved the subresource
89 protected boolean fIsXPointerResolved = false;
90
91 // Fixup xml:base and xml:lang attributes
92 protected boolean fFixupBase = false;
93 protected boolean fFixupLang = false;
94
95 // ************************************************************************
96 // Constructors
97 // ************************************************************************
98
99 /**
100 *
101 */
102 public XPointerHandler() {
103 super();
104
105 fXPointerParts = new Vector();
106 fSymbolTable = new SymbolTable();
107 }
108
109 public XPointerHandler(SymbolTable symbolTable,
110 XMLErrorHandler errorHandler, XMLErrorReporter errorReporter) {
111 super();
112
113 fXPointerParts = new Vector();
114 fSymbolTable = symbolTable;
115 fErrorHandler = errorHandler;
116 fXPointerErrorReporter = errorReporter;
117 //fErrorReporter = errorReporter; // The XInclude ErrorReporter
118 }
119
120 // ************************************************************************
121 // Implementation of the XPointerProcessor interface.
122 // ************************************************************************
123
124 /**
125 * Parses the XPointer framework expression and delegates scheme specific parsing.
126 *
127 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
128 */
129 public void parseXPointer(String xpointer) throws XNIException {
130
131 // Initialize
132 init();
133
134 // tokens
135 final Tokens tokens = new Tokens(fSymbolTable);
136
137 // scanner
138 Scanner scanner = new Scanner(fSymbolTable) {
139 protected void addToken(Tokens tokens, int token)
140 throws XNIException {
141 if (token == Tokens.XPTRTOKEN_OPEN_PAREN
142 || token == Tokens.XPTRTOKEN_CLOSE_PAREN
143 || token == Tokens.XPTRTOKEN_SCHEMENAME
144 || token == Tokens.XPTRTOKEN_SCHEMEDATA
145 || token == Tokens.XPTRTOKEN_SHORTHAND) {
146 super.addToken(tokens, token);
147 return;
148 }
149 reportError("InvalidXPointerToken", new Object[] { tokens
150 .getTokenString(token) });
151 }
152 };
153
154 // scan the XPointer expression
155 int length = xpointer.length();
156 boolean success = scanner.scanExpr(fSymbolTable, tokens, xpointer, 0,
157 length);
158
159 if (!success)
160 reportError("InvalidXPointerExpression", new Object[] { xpointer });
161
162 while (tokens.hasMore()) {
163 int token = tokens.nextToken();
164
165 switch (token) {
166 case Tokens.XPTRTOKEN_SHORTHAND: {
167
168 // The shortHand name
169 token = tokens.nextToken();
170 String shortHandPointerName = tokens.getTokenString(token);
171
172 if (shortHandPointerName == null) {
173 reportError("InvalidXPointerExpression",
174 new Object[] { xpointer });
175 }
176
177 XPointerPart shortHandPointer = new ShortHandPointer(
178 fSymbolTable);
179 shortHandPointer.setSchemeName(shortHandPointerName);
180 fXPointerParts.add(shortHandPointer);
181 break;
182 }
183 case Tokens.XPTRTOKEN_SCHEMENAME: {
184
185 // Retreive the local name and prefix to form the scheme name
186 token = tokens.nextToken();
187 String prefix = tokens.getTokenString(token);
188 token = tokens.nextToken();
189 String localName = tokens.getTokenString(token);
190
191 String schemeName = prefix + localName;
192
193 // The next character should be an open parenthesis
194 int openParenCount = 0;
195 int closeParenCount = 0;
196
197 token = tokens.nextToken();
198 String openParen = tokens.getTokenString(token);
199 if (openParen != "XPTRTOKEN_OPEN_PAREN") {
200
201 // can not have more than one ShortHand Pointer
202 if (token == Tokens.XPTRTOKEN_SHORTHAND) {
203 reportError("MultipleShortHandPointers",
204 new Object[] { xpointer });
205 } else {
206 reportError("InvalidXPointerExpression",
207 new Object[] { xpointer });
208 }
209 }
210 openParenCount++;
211
212 // followed by zero or more ( and the schemeData
213 String schemeData = null;
214 while (tokens.hasMore()) {
215 token = tokens.nextToken();
216 schemeData = tokens.getTokenString(token);
217 if (schemeData != "XPTRTOKEN_OPEN_PAREN") {
218 break;
219 }
220 openParenCount++;
221 }
222 token = tokens.nextToken();
223 schemeData = tokens.getTokenString(token);
224
225 // followed by the same number of )
226 token = tokens.nextToken();
227 String closeParen = tokens.getTokenString(token);
228 if (closeParen != "XPTRTOKEN_CLOSE_PAREN") {
229 reportError("SchemeDataNotFollowedByCloseParenthesis",
230 new Object[] { xpointer });
231 }
232 closeParenCount++;
233
234 while (tokens.hasMore()) {
235 if (tokens.getTokenString(tokens.peekToken()) != "XPTRTOKEN_OPEN_PAREN") {
236 break;
237 }
238 closeParenCount++;
239 }
240
241 // check if the number of open parenthesis are equal to the number of close parenthesis
242 if (openParenCount != closeParenCount) {
243 reportError("UnbalancedParenthesisInXPointerExpression",
244 new Object[] { xpointer,
245 new Integer(openParenCount),
246 new Integer(closeParenCount) });
247 }
248
249 // Perform scheme specific parsing of the pointer part
250 if (schemeName.equals(ELEMENT_SCHEME_NAME)) {
251 XPointerPart elementSchemePointer = new ElementSchemePointer(
252 fSymbolTable, fErrorReporter);
253 elementSchemePointer.setSchemeName(schemeName);
254 elementSchemePointer.setSchemeData(schemeData);
255
256 // If an exception occurs while parsing the element() scheme expression
257 // ignore it and move on to the next pointer part
258 try {
259 elementSchemePointer.parseXPointer(schemeData);
260 fXPointerParts.add(elementSchemePointer);
261 } catch (XNIException e) {
262 // Re-throw the XPointer element() scheme syntax error.
263 throw new XNIException (e);
264 }
265
266 } else {
267 // ????
268 reportWarning("SchemeUnsupported",
269 new Object[] { schemeName });
270 }
271
272 break;
273 }
274 default:
275 reportError("InvalidXPointerExpression",
276 new Object[] { xpointer });
277 }
278 }
279
280 }
281
282 /**
283 *
284 * @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)
285 */
286 public boolean resolveXPointer(QName element, XMLAttributes attributes,
287 Augmentations augs, int event) throws XNIException {
288 boolean resolved = false;
289
290 // The result of the first pointer part whose evaluation identifies
291 // one or more subresources is reported by the XPointer processor as the
292 // result of the pointer as a whole, and evaluation stops.
293 // In our implementation, typically the first xpointer scheme that
294 // matches an element is the document is considered.
295 // If the pointer part resolved then use it, else search for the fragment
296 // using next pointer part from lef-right.
297 if (!fFoundMatchingPtrPart) {
298
299 // for each element, attempt to resolve it against each pointer part
300 // in the XPointer expression until a matching element is found.
301 for (int i = 0; i < fXPointerParts.size(); i++) {
302
303 fXPointerPart = (XPointerPart) fXPointerParts.get(i);
304
305 if (fXPointerPart.resolveXPointer(element, attributes, augs,
306 event)) {
307 fFoundMatchingPtrPart = true;
308 resolved = true;
309 }
310 }
311 } else {
312 if (fXPointerPart.resolveXPointer(element, attributes, augs, event)) {
313 resolved = true;
314 }
315 }
316
317 if (!fIsXPointerResolved) {
318 fIsXPointerResolved = resolved;
319 }
320
321 return resolved;
322 }
323
324 /**
325 * Returns true if the Node fragment is resolved.
326 *
327 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
328 */
329 public boolean isFragmentResolved() throws XNIException {
330 boolean resolved = (fXPointerPart != null) ? fXPointerPart.isFragmentResolved()
331 : false;
332
333 if (!fIsXPointerResolved) {
334 fIsXPointerResolved = resolved;
335 }
336
337 return resolved;
338 }
339
340 /**
341 * Returns true if the XPointer expression resolves to a non-element child
342 * of the current resource fragment.
343 *
344 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved()
345 *
346 */
347 public boolean isChildFragmentResolved() throws XNIException {
348 boolean resolved = (fXPointerPart != null) ? fXPointerPart
349 .isChildFragmentResolved() : false;
350 return resolved;
351 }
352
353 /**
354 * Returns true if the XPointer successfully found a sub-resource .
355 *
356 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
357 */
358 public boolean isXPointerResolved() throws XNIException {
359 return fIsXPointerResolved;
360 }
361
362 /**
363 * Returns the pointer part used to resolve the document fragment.
364 *
365 * @return String - The pointer part used to resolve the document fragment.
366 */
367 public XPointerPart getXPointerPart() {
368 return fXPointerPart;
369 }
370
371 /**
372 * Reports XPointer Errors
373 *
374 */
375 private void reportError(String key, Object[] arguments)
376 throws XNIException {
377 /*
378 fXPointerErrorReporter.reportError(
379 XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
380 XMLErrorReporter.SEVERITY_ERROR);
381 */
382 throw new XNIException((fErrorReporter
383 .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
384 .formatMessage(fErrorReporter.getLocale(), key, arguments));
385 }
386
387 /**
388 * Reports XPointer Warnings
389 *
390 */
391 private void reportWarning(String key, Object[] arguments)
392 throws XNIException {
393 fXPointerErrorReporter.reportError(
394 XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
395 XMLErrorReporter.SEVERITY_WARNING);
396 }
397
398 /**
399 * Initializes error handling objects
400 *
401 */
402 protected void initErrorReporter() {
403 if (fXPointerErrorReporter == null) {
404 fXPointerErrorReporter = new XMLErrorReporter();
405 }
406 if (fErrorHandler == null) {
407 fErrorHandler = new XPointerErrorHandler();
408 }
409 /*
410 fXPointerErrorReporter.setProperty(Constants.XERCES_PROPERTY_PREFIX
411 + Constants.ERROR_HANDLER_PROPERTY, fErrorHandler);
412 */
413 fXPointerErrorReporter.putMessageFormatter(
414 XPointerMessageFormatter.XPOINTER_DOMAIN,
415 new XPointerMessageFormatter());
416 }
417
418 /**
419 * Initializes the XPointer Processor;
420 */
421 protected void init() {
422 fXPointerParts.clear();
423 fXPointerPart = null;
424 fFoundMatchingPtrPart = false;
425 fIsXPointerResolved = false;
426 //fFixupBase = false;
427 //fFixupLang = false;
428
429 initErrorReporter();
430 }
431
432 /**
433 * Returns a Vector of XPointerPart objects
434 *
435 * @return A Vector of XPointerPart objects.
436 */
437 public Vector getPointerParts() {
438 return fXPointerParts;
439 }
440
441 /**
442 * List of XPointer Framework tokens.
443 *
444 * @xerces.internal
445 *
446 */
447 private final class Tokens {
448
449 /**
450 * XPointer Framework tokens
451 * [1] Pointer ::= Shorthand | SchemeBased
452 * [2] Shorthand ::= NCName
453 * [3] SchemeBased ::= PointerPart (S? PointerPart)*
454 * [4] PointerPart ::= SchemeName '(' SchemeData ')'
455 * [5] SchemeName ::= QName
456 * [6] SchemeData ::= EscapedData*
457 * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
458 * [8] NormalChar ::= UnicodeChar - [()^]
459 * [9] UnicodeChar ::= [#x0-#x10FFFF]
460 *
461 */
462 private static final int XPTRTOKEN_OPEN_PAREN = 0,
463 XPTRTOKEN_CLOSE_PAREN = 1, XPTRTOKEN_SHORTHAND = 2,
464 XPTRTOKEN_SCHEMENAME = 3, XPTRTOKEN_SCHEMEDATA = 4;
465
466 // Token names
467 private final String[] fgTokenNames = { "XPTRTOKEN_OPEN_PAREN",
468 "XPTRTOKEN_CLOSE_PAREN", "XPTRTOKEN_SHORTHAND",
469 "XPTRTOKEN_SCHEMENAME", "XPTRTOKEN_SCHEMEDATA" };
470
471 // Token count
472 private static final int INITIAL_TOKEN_COUNT = 1 << 8;
473
474 private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
475
476 private int fTokenCount = 0;
477
478 // Current token position
479 private int fCurrentTokenIndex;
480
481 private SymbolTable fSymbolTable;
482
483 private Hashtable fTokenNames = new Hashtable();
484
485 /**
486 * Constructor
487 *
488 * @param symbolTable SymbolTable
489 */
490 private Tokens(SymbolTable symbolTable) {
491 fSymbolTable = symbolTable;
492
493 fTokenNames.put(new Integer(XPTRTOKEN_OPEN_PAREN),
494 "XPTRTOKEN_OPEN_PAREN");
495 fTokenNames.put(new Integer(XPTRTOKEN_CLOSE_PAREN),
496 "XPTRTOKEN_CLOSE_PAREN");
497 fTokenNames.put(new Integer(XPTRTOKEN_SHORTHAND),
498 "XPTRTOKEN_SHORTHAND");
499 fTokenNames.put(new Integer(XPTRTOKEN_SCHEMENAME),
500 "XPTRTOKEN_SCHEMENAME");
501 fTokenNames.put(new Integer(XPTRTOKEN_SCHEMEDATA),
502 "XPTRTOKEN_SCHEMEDATA");
503 }
504
505 /**
506 * Returns the token String
507 * @param token The index of the token
508 * @return String The token string
509 */
510 private String getTokenString(int token) {
511 return (String) fTokenNames.get(new Integer(token));
512 }
513
514 /**
515 * Add the specified string as a token
516 *
517 * @param token The token string
518 */
519 private void addToken(String tokenStr) {
520 Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
521 if (tokenInt == null) {
522 tokenInt = new Integer(fTokenNames.size());
523 fTokenNames.put(tokenInt, tokenStr);
524 }
525 addToken(tokenInt.intValue());
526 }
527
528 /**
529 * Add the specified int token
530 *
531 * @param token The int specifying the token
532 */
533 private void addToken(int token) {
534 try {
535 fTokens[fTokenCount] = token;
536 } catch (ArrayIndexOutOfBoundsException ex) {
537 int[] oldList = fTokens;
538 fTokens = new int[fTokenCount << 1];
539 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
540 fTokens[fTokenCount] = token;
541 }
542 fTokenCount++;
543 }
544
545 /**
546 * Resets the current position to the head of the token list.
547 */
548 private void rewind() {
549 fCurrentTokenIndex = 0;
550 }
551
552 /**
553 * Returns true if the {@link #getNextToken()} method
554 * returns a valid token.
555 */
556 private boolean hasMore() {
557 return fCurrentTokenIndex < fTokenCount;
558 }
559
560 /**
561 * Obtains the token at the current position, then advance
562 * the current position by one.
563 *
564 * throws If there's no such next token, this method throws
565 * <tt>new XNIException("XPointerProcessingError");</tt>.
566 */
567 private int nextToken() throws XNIException {
568 if (fCurrentTokenIndex == fTokenCount) {
569 reportError("XPointerProcessingError", null);
570 }
571 return fTokens[fCurrentTokenIndex++];
572 }
573
574 /**
575 * Obtains the token at the current position, without advancing
576 * the current position.
577 *
578 * If there's no such next token, this method throws
579 * <tt>new XNIException("XPointerProcessingError");</tt>.
580 */
581 private int peekToken() throws XNIException {
582 if (fCurrentTokenIndex == fTokenCount) {
583 reportError("XPointerProcessingError", null);
584 }
585 return fTokens[fCurrentTokenIndex];
586 }
587
588 /**
589 * Obtains the token at the current position as a String.
590 *
591 * If there's no current token or if the current token
592 * is not a string token, this method throws
593 * If there's no such next token, this method throws
594 * <tt>new XNIException("XPointerProcessingError");</tt>.
595 */
596 private String nextTokenAsString() throws XNIException {
597 String tokenStrint = getTokenString(nextToken());
598 if (tokenStrint == null) {
599 reportError("XPointerProcessingError", null);
600 }
601 return tokenStrint;
602 }
603 }
604
605 /**
606 *
607 * The XPointer expression scanner. Scans the XPointer framework expression.
608 *
609 * @xerces.internal
610 *
611 */
612 private class Scanner {
613
614 /**
615 * 7-bit ASCII subset
616 *
617 * 0 1 2 3 4 5 6 7 8 9 A B C D E F
618 * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
619 * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
620 * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
621 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
622 * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
623 * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
624 * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
625 * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
626 */
627 private static final byte CHARTYPE_INVALID = 0, // invalid XML character
628 CHARTYPE_OTHER = 1, // not special - one of "#%&;?\`{}~" or DEL
629 CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
630 CHARTYPE_CARRET = 3, // ^
631 CHARTYPE_OPEN_PAREN = 4, // '(' (0x28)
632 CHARTYPE_CLOSE_PAREN = 5, // ')' (0x29)
633 CHARTYPE_MINUS = 6, // '-' (0x2D)
634 CHARTYPE_PERIOD = 7, // '.' (0x2E)
635 CHARTYPE_SLASH = 8, // '/' (0x2F)
636 CHARTYPE_DIGIT = 9, // '0'-'9' (0x30 to 0x39)
637 CHARTYPE_COLON = 10, // ':' (0x3A)
638 CHARTYPE_EQUAL = 11, // '=' (0x3D)
639 CHARTYPE_LETTER = 12, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
640 CHARTYPE_UNDERSCORE = 13, // '_' (0x5F)
641 CHARTYPE_NONASCII = 14; // Non-ASCII Unicode codepoint (>= 0x80)
642
643 private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
644 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
645 2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 6, 7, 8, 9, 9, 9, 9, 9,
646 9, 9, 9, 9, 9, 10, 1, 1, 11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
647 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
648 12, 12, 12, 12, 1, 1, 1, 3, 13, 1, 12, 12, 12, 12, 12, 12, 12,
649 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
650 12, 12, 12, 1, 1, 1, 1, 1 };
651
652 //
653 // Data
654 //
655 /** Symbol table. */
656 private SymbolTable fSymbolTable;
657
658 /**
659 * Constructs an XPointer Framework expression scanner.
660 *
661 * @param symbolTable SymbolTable
662 */
663 private Scanner(SymbolTable symbolTable) {
664 // save pool and tokens
665 fSymbolTable = symbolTable;
666
667 } // <init>(SymbolTable)
668
669 /**
670 * Scans the XPointer Expression
671 *
672 */
673 private boolean scanExpr(SymbolTable symbolTable, Tokens tokens,
674 String data, int currentOffset, int endOffset)
675 throws XNIException {
676
677 int ch;
678 int openParen = 0;
679 int closeParen = 0;
680 int nameOffset, dataOffset;
681 boolean isQName = false;
682 String name = null;
683 String prefix = null;
684 String schemeData = null;
685 StringBuffer schemeDataBuff = new StringBuffer();
686
687 while (true) {
688
689 if (currentOffset == endOffset) {
690 break;
691 }
692 ch = data.charAt(currentOffset);
693
694 //
695 while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
696 if (++currentOffset == endOffset) {
697 break;
698 }
699 ch = data.charAt(currentOffset);
700 }
701 if (currentOffset == endOffset) {
702 break;
703 }
704
705 //
706 // [1] Pointer ::= Shorthand | SchemeBased
707 // [2] Shorthand ::= NCName
708 // [3] SchemeBased ::= PointerPart (S? PointerPart)*
709 // [4] PointerPart ::= SchemeName '(' SchemeData ')'
710 // [5] SchemeName ::= QName
711 // [6] SchemeData ::= EscapedData*
712 // [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
713 // [8] NormalChar ::= UnicodeChar - [()^]
714 // [9] UnicodeChar ::= [#x0-#x10FFFF]
715 // [?] QName ::= (NCName ':')? NCName
716 // [?] NCName ::= (Letter | '_') (NCNameChar)*
717 // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar')
718 // [?] Letter ::= [A-Za-z] (ascii subset of 'Letter')
719 // [?] Digit ::= [0-9] (ascii subset of 'Digit')
720 //
721 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
722 : fASCIICharMap[ch];
723
724 switch (chartype) {
725
726 case CHARTYPE_OPEN_PAREN: // '('
727 addToken(tokens, Tokens.XPTRTOKEN_OPEN_PAREN);
728 openParen++;
729 ++currentOffset;
730 break;
731
732 case CHARTYPE_CLOSE_PAREN: // ')'
733 addToken(tokens, Tokens.XPTRTOKEN_CLOSE_PAREN);
734 closeParen++;
735 ++currentOffset;
736 break;
737
738 case CHARTYPE_CARRET:
739 case CHARTYPE_COLON:
740 case CHARTYPE_DIGIT:
741 case CHARTYPE_EQUAL:
742 case CHARTYPE_LETTER:
743 case CHARTYPE_MINUS:
744 case CHARTYPE_NONASCII:
745 case CHARTYPE_OTHER:
746 case CHARTYPE_PERIOD:
747 case CHARTYPE_SLASH:
748 case CHARTYPE_UNDERSCORE:
749 case CHARTYPE_WHITESPACE:
750 // Scanning SchemeName | Shorthand
751 if (openParen == 0) {
752 nameOffset = currentOffset;
753 currentOffset = scanNCName(data, endOffset,
754 currentOffset);
755
756 if (currentOffset == nameOffset) {
757 reportError("InvalidShortHandPointer",
758 new Object[] { data });
759 return false;
760 }
761
762 if (currentOffset < endOffset) {
763 ch = data.charAt(currentOffset);
764 } else {
765 ch = -1;
766 }
767
768 name = symbolTable.addSymbol(data.substring(nameOffset,
769 currentOffset));
770 prefix = XMLSymbols.EMPTY_STRING;
771
772 // The name is a QName => a SchemeName
773 if (ch == ':') {
774 if (++currentOffset == endOffset) {
775 return false;
776 }
777
778 ch = data.charAt(currentOffset);
779 prefix = name;
780 nameOffset = currentOffset;
781 currentOffset = scanNCName(data, endOffset,
782 currentOffset);
783
784 if (currentOffset == nameOffset) {
785 return false;
786 }
787
788 if (currentOffset < endOffset) {
789 ch = data.charAt(currentOffset);
790 } else {
791 ch = -1;
792 }
793
794 isQName = true;
795 name = symbolTable.addSymbol(data.substring(
796 nameOffset, currentOffset));
797 }
798
799 // REVISIT:
800 if (currentOffset != endOffset) {
801 addToken(tokens, Tokens.XPTRTOKEN_SCHEMENAME);
802 tokens.addToken(prefix);
803 tokens.addToken(name);
804 isQName = false;
805 } else if (currentOffset == endOffset) {
806 // NCName => Shorthand
807 addToken(tokens, Tokens.XPTRTOKEN_SHORTHAND);
808 tokens.addToken(name);
809 isQName = false;
810 }
811
812 // reset open/close paren for the next pointer part
813 closeParen = 0;
814
815 break;
816
817 } else if (openParen > 0 && closeParen == 0 && name != null) {
818 // Scanning SchemeData
819 dataOffset = currentOffset;
820 currentOffset = scanData(data, schemeDataBuff,
821 endOffset, currentOffset);
822
823 if (currentOffset == dataOffset) {
824 reportError("InvalidSchemeDataInXPointer",
825 new Object[] { data });
826 return false;
827 }
828
829 if (currentOffset < endOffset) {
830 ch = data.charAt(currentOffset);
831 } else {
832 ch = -1;
833 }
834
835 schemeData = symbolTable.addSymbol(schemeDataBuff
836 .toString());
837 addToken(tokens, Tokens.XPTRTOKEN_SCHEMEDATA);
838 tokens.addToken(schemeData);
839
840 // reset open/close paren for the next pointer part
841 openParen = 0;
842 schemeDataBuff.delete(0, schemeDataBuff.length());
843
844 } else {
845 // ex. schemeName()
846 // Should we throw an exception with a more suitable message instead??
847 return false;
848 }
849 }
850 } // end while
851 return true;
852 }
853
854 /**
855 * Scans a NCName.
856 * From Namespaces in XML
857 * [5] NCName ::= (Letter | '_') (NCNameChar)*
858 * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
859 *
860 * @param data A String containing the XPointer expression
861 * @param endOffset The int XPointer expression length
862 * @param currentOffset An int representing the current position of the XPointer expression pointer
863 */
864 private int scanNCName(String data, int endOffset, int currentOffset) {
865 int ch = data.charAt(currentOffset);
866 if (ch >= 0x80) {
867 if (!XMLChar.isNameStart(ch)) {
868 return currentOffset;
869 }
870 } else {
871 byte chartype = fASCIICharMap[ch];
872 if (chartype != CHARTYPE_LETTER
873 && chartype != CHARTYPE_UNDERSCORE) {
874 return currentOffset;
875 }
876 }
877
878 //while (currentOffset++ < endOffset) {
879 while (++currentOffset < endOffset) {
880 ch = data.charAt(currentOffset);
881 if (ch >= 0x80) {
882 if (!XMLChar.isName(ch)) {
883 break;
884 }
885 } else {
886 byte chartype = fASCIICharMap[ch];
887 if (chartype != CHARTYPE_LETTER
888 && chartype != CHARTYPE_DIGIT
889 && chartype != CHARTYPE_PERIOD
890 && chartype != CHARTYPE_MINUS
891 && chartype != CHARTYPE_UNDERSCORE) {
892 break;
893 }
894 }
895 }
896 return currentOffset;
897 }
898
899 /**
900 * Scans the SchemeData.
901 * [6] SchemeData ::= EscapedData*
902 * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
903 * [8] NormalChar ::= UnicodeChar - [()^]
904 * [9] UnicodeChar ::= [#x0-#x10FFFF]
905 *
906 */
907 private int scanData(String data, StringBuffer schemeData,
908 int endOffset, int currentOffset) {
909 while (true) {
910
911 if (currentOffset == endOffset) {
912 break;
913 }
914
915 int ch = data.charAt(currentOffset);
916 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
917 : fASCIICharMap[ch];
918
919 if (chartype == CHARTYPE_OPEN_PAREN) {
920 schemeData.append(ch);
921 //schemeData.append(Tokens.XPTRTOKEN_OPEN_PAREN);
922 currentOffset = scanData(data, schemeData, endOffset,
923 ++currentOffset);
924 if (currentOffset == endOffset) {
925 return currentOffset;
926 }
927
928 ch = data.charAt(currentOffset);
929 chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
930 : fASCIICharMap[ch];
931
932 if (chartype != CHARTYPE_CLOSE_PAREN) {
933 return endOffset;
934 }
935 schemeData.append((char) ch);
936 ++currentOffset;//
937
938 } else if (chartype == CHARTYPE_CLOSE_PAREN) {
939 return currentOffset;
940
941 } else if (chartype == CHARTYPE_CARRET) {
942 ch = data.charAt(++currentOffset);
943 chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
944 : fASCIICharMap[ch];
945
946 if (chartype != CHARTYPE_CARRET
947 && chartype != CHARTYPE_OPEN_PAREN
948 && chartype != CHARTYPE_CLOSE_PAREN) {
949 break;
950 }
951 schemeData.append((char) ch);
952 ++currentOffset;
953
954 } else {
955 schemeData.append((char) ch);
956 ++currentOffset;//
957 }
958 }
959
960 return currentOffset;
961 }
962
963 //
964 // Protected methods
965 //
966
967 /**
968 * This method adds the specified token to the token list. By
969 * default, this method allows all tokens. However, subclasses
970 * of the XPathExprScanner can override this method in order
971 * to disallow certain tokens from being used in the scanned
972 * XPath expression. This is a convenient way of allowing only
973 * a subset of XPath.
974 */
975 protected void addToken(Tokens tokens, int token) throws XNIException {
976 tokens.addToken(token);
977 } // addToken(int)
978
979 } // class Scanner
980
981 // ************************************************************************
982 // Overridden XMLDocumentHandler methods
983 // ************************************************************************
984 /**
985 * If the comment is a child of a matched element, then pass else return.
986 *
987 * @param text The text in the comment.
988 * @param augs Additional information that may include infoset augmentations
989 *
990 * @exception XNIException
991 * Thrown by application to signal an error.
992 */
993 public void comment(XMLString text, Augmentations augs) throws XNIException {
994 if (!isChildFragmentResolved()) {
995 return;
996 }
997 super.comment(text, augs);
998 }
999
1000 /**
1001 * A processing instruction. Processing instructions consist of a
1002 * target name and, optionally, text data. The data is only meaningful
1003 * to the application.
1004 * <p>
1005 * Typically, a processing instruction's data will contain a series
1006 * of pseudo-attributes. These pseudo-attributes follow the form of
1007 * element attributes but are <strong>not</strong> parsed or presented
1008 * to the application as anything other than text. The application is
1009 * responsible for parsing the data.
1010 *
1011 * @param target The target.
1012 * @param data The data or null if none specified.
1013 * @param augs Additional information that may include infoset augmentations
1014 *
1015 * @exception XNIException
1016 * Thrown by handler to signal an error.
1017 */
1018 public void processingInstruction(String target, XMLString data,
1019 Augmentations augs) throws XNIException {
1020 if (!isChildFragmentResolved()) {
1021 return;
1022 }
1023 super.processingInstruction(target, data, augs);
1024 }
1025
1026 /**
1027 * The start of an element.
1028 *
1029 * @param element The name of the element.
1030 * @param attributes The element attributes.
1031 * @param augs Additional information that may include infoset augmentations
1032 *
1033 * @exception XNIException
1034 * Thrown by handler to signal an error.
1035 */
1036 public void startElement(QName element, XMLAttributes attributes,
1037 Augmentations augs) throws XNIException {
1038 if (!resolveXPointer(element, attributes, augs,
1039 XPointerPart.EVENT_ELEMENT_START)) {
1040
1041 // xml:base and xml:lang processing
1042 if (fFixupBase) {
1043 processXMLBaseAttributes(attributes);
1044 }
1045 if (fFixupLang) {
1046 processXMLLangAttributes(attributes);
1047 }
1048
1049 // set the context invalid if the element till an element from the result infoset is included
1050 fNamespaceContext.setContextInvalid();
1051
1052 return;
1053 }
1054 super.startElement(element, attributes, augs);
1055 }
1056
1057 /**
1058 * An empty element.
1059 *
1060 * @param element The name of the element.
1061 * @param attributes The element attributes.
1062 * @param augs Additional information that may include infoset augmentations
1063 *
1064 * @exception XNIException
1065 * Thrown by handler to signal an error.
1066 */
1067 public void emptyElement(QName element, XMLAttributes attributes,
1068 Augmentations augs) throws XNIException {
1069 if (!resolveXPointer(element, attributes, augs,
1070 XPointerPart.EVENT_ELEMENT_EMPTY)) {
1071 // xml:base and xml:lang processing
1072 if (fFixupBase) {
1073 processXMLBaseAttributes(attributes);
1074 }
1075 if (fFixupLang) {
1076 processXMLLangAttributes(attributes);
1077 }
1078 // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1079
1080 // set the context invalid if the element till an element from the result infoset is included
1081 fNamespaceContext.setContextInvalid();
1082 return;
1083 }
1084 super.emptyElement(element, attributes, augs);
1085 }
1086
1087 /**
1088 * Character content.
1089 *
1090 * @param text The content.
1091 * @param augs Additional information that may include infoset augmentations
1092 *
1093 * @exception XNIException
1094 * Thrown by handler to signal an error.
1095 */
1096 public void characters(XMLString text, Augmentations augs)
1097 throws XNIException {
1098 if (!isChildFragmentResolved()) {
1099 return;
1100 }
1101 super.characters(text, augs);
1102 }
1103
1104 /**
1105 * Ignorable whitespace. For this method to be called, the document
1106 * source must have some way of determining that the text containing
1107 * only whitespace characters should be considered ignorable. For
1108 * example, the validator can determine if a length of whitespace
1109 * characters in the document are ignorable based on the element
1110 * content model.
1111 *
1112 * @param text The ignorable whitespace.
1113 * @param augs Additional information that may include infoset augmentations
1114 *
1115 * @exception XNIException
1116 * Thrown by handler to signal an error.
1117 */
1118 public void ignorableWhitespace(XMLString text, Augmentations augs)
1119 throws XNIException {
1120 if (!isChildFragmentResolved()) {
1121 return;
1122 }
1123 super.ignorableWhitespace(text, augs);
1124 }
1125
1126 /**
1127 * The end of an element.
1128 *
1129 * @param element The name of the element.
1130 * @param augs Additional information that may include infoset augmentations
1131 *
1132 * @exception XNIException
1133 * Thrown by handler to signal an error.
1134 */
1135 public void endElement(QName element, Augmentations augs)
1136 throws XNIException {
1137 if (!resolveXPointer(element, null, augs,
1138 XPointerPart.EVENT_ELEMENT_END)) {
1139
1140 // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1141 return;
1142 }
1143 super.endElement(element, augs);
1144 }
1145
1146 /**
1147 * The start of a CDATA section.
1148 *
1149 * @param augs Additional information that may include infoset augmentations
1150 *
1151 * @exception XNIException
1152 * Thrown by handler to signal an error.
1153 */
1154 public void startCDATA(Augmentations augs) throws XNIException {
1155 if (!isChildFragmentResolved()) {
1156 return;
1157 }
1158 super.startCDATA(augs);
1159 }
1160
1161 /**
1162 * The end of a CDATA section.
1163 *
1164 * @param augs Additional information that may include infoset augmentations
1165 *
1166 * @exception XNIException
1167 * Thrown by handler to signal an error.
1168 */
1169 public void endCDATA(Augmentations augs) throws XNIException {
1170 if (!isChildFragmentResolved()) {
1171 return;
1172 }
1173 super.endCDATA(augs);
1174 }
1175
1176 // ************************************************************************
1177 // Overridden XMLComponent methods
1178 // ************************************************************************
1179 /**
1180 * <p>
1181 * Sets the value of a property. This method is called by the component
1182 * manager any time after reset when a property changes value.
1183 * </p>
1184 * <strong>Note:</strong> Components should silently ignore properties
1185 * that do not affect the operation of the component.
1186 *
1187 * @param propertyId The property identifier.
1188 * @param value The value of the property.
1189 *
1190 * @throws XMLConfigurationException Thrown for configuration error.
1191 * In general, components should
1192 * only throw this exception if
1193 * it is <strong>really</strong>
1194 * a critical error.
1195 */
1196 public void setProperty(String propertyId, Object value)
1197 throws XMLConfigurationException {
1198
1199 // Error reporter
1200 if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1201 + Constants.ERROR_REPORTER_PROPERTY) {
1202 if (value != null) {
1203 fXPointerErrorReporter = (XMLErrorReporter) value;
1204 } else {
1205 fXPointerErrorReporter = null;
1206 }
1207 }
1208
1209 // Error handler
1210 if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1211 + Constants.ERROR_HANDLER_PROPERTY) {
1212 if (value != null) {
1213 fErrorHandler = (XMLErrorHandler) value;
1214 } else {
1215 fErrorHandler = null;
1216 }
1217 }
1218
1219 // xml:lang
1220 if (propertyId == Constants.XERCES_FEATURE_PREFIX
1221 + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE) {
1222 if (value != null) {
1223 fFixupLang = ((Boolean)value).booleanValue();
1224 } else {
1225 fFixupLang = false;
1226 }
1227 }
1228
1229 // xml:base
1230 if (propertyId == Constants.XERCES_FEATURE_PREFIX
1231 + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE) {
1232 if (value != null) {
1233 fFixupBase = ((Boolean)value).booleanValue();
1234 } else {
1235 fFixupBase = false;
1236 }
1237 }
1238
1239 //
1240 if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1241 + Constants.NAMESPACE_CONTEXT_PROPERTY) {
1242 fNamespaceContext = (XIncludeNamespaceSupport) value;
1243 }
1244
1245 super.setProperty(propertyId, value);
1246 }
1247
1248}