001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.xerces.xpointer;
018:
019: import java.util.Hashtable;
020:
021: import org.apache.xerces.impl.XMLErrorReporter;
022: import org.apache.xerces.util.SymbolTable;
023: import org.apache.xerces.util.XMLChar;
024: import org.apache.xerces.xni.Augmentations;
025: import org.apache.xerces.xni.QName;
026: import org.apache.xerces.xni.XMLAttributes;
027: import org.apache.xerces.xni.XNIException;
028: import org.apache.xerces.xni.parser.XMLErrorHandler;
029:
030: /**
031: * <p>
032: * Implements the XPointerPart interface for element() scheme specific processing.
033: * </p>
034: *
035: * @xerces.internal
036: *
037: * @version $Id: ElementSchemePointer.java 447248 2006-09-18 05:25:21Z mrglavas $
038: *
039: */
040: class ElementSchemePointer implements XPointerPart {
041:
042: // Fields
043:
044: // The Scheme Name i.e element
045: private String fSchemeName;
046:
047: // The scheme Data
048: private String fSchemeData;
049:
050: // The scheme Data & child sequence
051: private String fShortHandPointerName;
052:
053: // Should we attempt to resolve the ChildSequence from the
054: // current element position. If a ShortHand Pointer is present
055: // attempt to resolve relative to the short hand pointer.
056: private boolean fIsResolveElement = false;
057:
058: // Has the element been found
059: private boolean fIsElementFound = false;
060:
061: // Was only an empty element found
062: private boolean fWasOnlyEmptyElementFound = false;
063:
064: // If a shorthand pointer is present and resolved
065: boolean fIsShortHand = false;
066:
067: // The depth at which the element was found
068: int fFoundDepth = 0;
069:
070: // The XPointer element child sequence
071: private int fChildSequence[];
072:
073: // The current child position
074: private int fCurrentChildPosition = 1;
075:
076: // The current child depth
077: private int fCurrentChildDepth = 0;
078:
079: // The current element's child sequence
080: private int fCurrentChildSequence[];;
081:
082: // Stores if the Fragment was resolved by the pointer
083: private boolean fIsFragmentResolved = false;
084:
085: // Stores if the Fragment was resolved by the pointer
086: private ShortHandPointer fShortHandPointer;
087:
088: // The XPointer Error reporter
089: protected XMLErrorReporter fErrorReporter;
090:
091: // The XPointer Error Handler
092: protected XMLErrorHandler fErrorHandler;
093:
094: //
095: private SymbolTable fSymbolTable;
096:
097: // ************************************************************************
098: // Constructors
099: // ************************************************************************
100: public ElementSchemePointer() {
101: }
102:
103: public ElementSchemePointer(SymbolTable symbolTable) {
104: fSymbolTable = symbolTable;
105: }
106:
107: public ElementSchemePointer(SymbolTable symbolTable,
108: XMLErrorReporter errorReporter) {
109: fSymbolTable = symbolTable;
110: fErrorReporter = errorReporter;
111: }
112:
113: // ************************************************************************
114: // XPointerPart implementation
115: // ************************************************************************
116:
117: /**
118: * Parses the XPointer expression and tokenizes it into Strings
119: * delimited by whitespace.
120: *
121: * @see org.apache.xerces.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
122: */
123: public void parseXPointer(String xpointer) throws XNIException {
124:
125: //
126: init();
127:
128: // tokens
129: final Tokens tokens = new Tokens(fSymbolTable);
130:
131: // scanner
132: Scanner scanner = new Scanner(fSymbolTable) {
133: protected void addToken(Tokens tokens, int token)
134: throws XNIException {
135: if (token == Tokens.XPTRTOKEN_ELEM_CHILD
136: || token == Tokens.XPTRTOKEN_ELEM_NCNAME) {
137: super .addToken(tokens, token);
138: return;
139: }
140: reportError("InvalidElementSchemeToken",
141: new Object[] { tokens.getTokenString(token) });
142: }
143: };
144:
145: // scan the element() XPointer expression
146: int length = xpointer.length();
147: boolean success = scanner.scanExpr(fSymbolTable, tokens,
148: xpointer, 0, length);
149:
150: if (!success) {
151: reportError("InvalidElementSchemeXPointer",
152: new Object[] { xpointer });
153: }
154:
155: // Initialize a temp arrays to the size of token count which should
156: // be atleast twice the size of child sequence, to hold the ChildSequence.
157: int tmpChildSequence[] = new int[tokens.getTokenCount() / 2 + 1];
158:
159: // the element depth
160: int i = 0;
161:
162: // Traverse the scanned tokens
163: while (tokens.hasMore()) {
164: int token = tokens.nextToken();
165:
166: switch (token) {
167: case Tokens.XPTRTOKEN_ELEM_NCNAME: {
168: // Note: Only a single ShortHand pointer can be present
169:
170: // The shortHand name
171: token = tokens.nextToken();
172: fShortHandPointerName = tokens.getTokenString(token);
173:
174: // Create a new ShortHandPointer
175: fShortHandPointer = new ShortHandPointer(fSymbolTable);
176: fShortHandPointer.setSchemeName(fShortHandPointerName);
177:
178: break;
179: }
180: case Tokens.XPTRTOKEN_ELEM_CHILD: {
181: tmpChildSequence[i] = tokens.nextToken();
182: i++;
183:
184: break;
185: }
186: default:
187: reportError("InvalidElementSchemeXPointer",
188: new Object[] { xpointer });
189: }
190: }
191:
192: // Initialize the arrays to the number of elements in the ChildSequence.
193: fChildSequence = new int[i];
194: fCurrentChildSequence = new int[i];
195: System.arraycopy(tmpChildSequence, 0, fChildSequence, 0, i);
196:
197: }
198:
199: /**
200: * Returns the scheme name i.e element
201: * @see org.apache.xerces.xpointer.XPointerPart#getSchemeName()
202: */
203: public String getSchemeName() {
204: return fSchemeName;
205: }
206:
207: /**
208: * Returns the scheme data
209: *
210: * @see org.apache.xerces.xpointer.XPointerPart#getSchemeData()
211: */
212: public String getSchemeData() {
213: return fSchemeData;
214: }
215:
216: /**
217: * Sets the scheme name
218: *
219: * @see org.apache.xerces.xpointer.XPointerPart#setSchemeName(java.lang.String)
220: */
221: public void setSchemeName(String schemeName) {
222: fSchemeName = schemeName;
223:
224: }
225:
226: /**
227: * Sets the scheme data
228: *
229: * @see org.apache.xerces.xpointer.XPointerPart#setSchemeData(java.lang.String)
230: */
231: public void setSchemeData(String schemeData) {
232: fSchemeData = schemeData;
233: }
234:
235: /**
236: * Responsible for resolving the element() scheme XPointer. If a ShortHand
237: * Pointer is present and it is successfully resolved and if a child
238: * sequence is present, the child sequence is resolved relative to it.
239: *
240: * @see org.apache.xerces.xpointer.XPointerProcessor#resolveXPointer(org.apache.xerces.xni.QName, org.apache.xerces.xni.XMLAttributes, org.apache.xerces.xni.Augmentations, int event)
241: */
242: public boolean resolveXPointer(QName element,
243: XMLAttributes attributes, Augmentations augs, int event)
244: throws XNIException {
245:
246: boolean isShortHandPointerResolved = false;
247:
248: // if a ChildSequence exisits, resolve child elements
249:
250: // if an element name exists
251: if (fShortHandPointerName != null) {
252: // resolve ShortHand Pointer
253: isShortHandPointerResolved = fShortHandPointer
254: .resolveXPointer(element, attributes, augs, event);
255: if (isShortHandPointerResolved) {
256: fIsResolveElement = true;
257: fIsShortHand = true;
258: } else {
259: fIsResolveElement = false;
260: }
261: } else {
262: fIsResolveElement = true;
263: }
264:
265: // Added here to skip the ShortHand pointer corresponding to
266: // an element if one exisits and start searching from its child
267: if (fChildSequence.length > 0) {
268: fIsFragmentResolved = matchChildSequence(element, event);
269: } else if (isShortHandPointerResolved
270: && fChildSequence.length <= 0) {
271: // if only a resolved shorthand pointer exists
272: fIsFragmentResolved = isShortHandPointerResolved;
273: } else {
274: fIsFragmentResolved = false;
275: }
276:
277: return fIsFragmentResolved;
278: }
279:
280: /**
281: * Matches the current element position in the document tree with the
282: * element position specified in the element XPointer scheme.
283: *
284: * @param event
285: * @return boolean - true if the current element position in the document
286: * tree matches theelement position specified in the element XPointer
287: * scheme.
288: */
289: protected boolean matchChildSequence(QName element, int event)
290: throws XNIException {
291:
292: // need to resize fCurrentChildSequence
293: if (fCurrentChildDepth >= fCurrentChildSequence.length) {
294: int tmpCurrentChildSequence[] = new int[fCurrentChildSequence.length];
295: System.arraycopy(fCurrentChildSequence, 0,
296: tmpCurrentChildSequence, 0,
297: fCurrentChildSequence.length);
298:
299: // Increase the size by a factor of 2 (?)
300: fCurrentChildSequence = new int[fCurrentChildDepth * 2];
301: System.arraycopy(tmpCurrentChildSequence, 0,
302: fCurrentChildSequence, 0,
303: tmpCurrentChildSequence.length);
304: }
305:
306: //
307: if (fIsResolveElement) {
308: // start
309: if (event == XPointerPart.EVENT_ELEMENT_START) {
310: fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition;
311: fCurrentChildDepth++;
312:
313: // reset the current child position
314: fCurrentChildPosition = 1;
315:
316: //if (!fSchemeNameFound) {
317: if ((fCurrentChildDepth <= fFoundDepth)
318: || (fFoundDepth == 0)) {
319: if (checkMatch()) {
320: fIsElementFound = true;
321: fFoundDepth = fCurrentChildDepth;
322: } else {
323: fIsElementFound = false;
324: fFoundDepth = 0;
325: }
326: }
327:
328: } else if (event == XPointerPart.EVENT_ELEMENT_END) {
329: if (fCurrentChildDepth == fFoundDepth) {
330: fIsElementFound = true;
331: } else if (((fCurrentChildDepth < fFoundDepth) && (fFoundDepth != 0))
332: || ((fCurrentChildDepth > fFoundDepth) // or empty element found
333: && (fFoundDepth == 0))) {
334: fIsElementFound = false;
335: }
336:
337: // reset array position of last child
338: fCurrentChildSequence[fCurrentChildDepth] = 0;
339:
340: fCurrentChildDepth--;
341: fCurrentChildPosition = fCurrentChildSequence[fCurrentChildDepth] + 1;
342:
343: } else if (event == XPointerPart.EVENT_ELEMENT_EMPTY) {
344:
345: fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition;
346: fCurrentChildPosition++;
347:
348: // Donot check for empty elements if the empty element is
349: // a child of a found parent element
350: if (checkMatch()) {
351: if (!fIsElementFound) {
352: fWasOnlyEmptyElementFound = true;
353: } else {
354: fWasOnlyEmptyElementFound = false;
355: }
356: fIsElementFound = true;
357: } else {
358: fIsElementFound = false;
359: fWasOnlyEmptyElementFound = false;
360: }
361: }
362: }
363:
364: return fIsElementFound;
365: }
366:
367: /**
368: * Matches the current position of the element being visited by checking
369: * its position and previous elements against the element XPointer expression.
370: * If a match is found it return true else false.
371: *
372: * @return boolean
373: */
374: protected boolean checkMatch() {
375: // If the number of elements in the ChildSequence is greater than the
376: // current child depth, there is not point in checking further
377: if (!fIsShortHand) {
378: // If a shorthand pointer is not present traverse the children
379: // and compare
380: if (fChildSequence.length <= fCurrentChildDepth + 1) {
381:
382: for (int i = 0; i < fChildSequence.length; i++) {
383: if (fChildSequence[i] != fCurrentChildSequence[i]) {
384: return false;
385: }
386: }
387: } else {
388: return false;
389: }
390: } else {
391: // If a shorthand pointer is present traverse the children
392: // ignoring the first element of the CurrenChildSequence which
393: // contains the ShortHand pointer element and compare
394: if (fChildSequence.length <= fCurrentChildDepth + 1) {
395:
396: for (int i = 0; i < fChildSequence.length; i++) {
397: // ensure fCurrentChildSequence is large enough
398: if (fCurrentChildSequence.length < i + 2) {
399: return false;
400: }
401:
402: // ignore the first element of fCurrentChildSequence
403: if (fChildSequence[i] != fCurrentChildSequence[i + 1]) {
404: return false;
405: }
406: }
407: } else {
408: return false;
409: }
410:
411: }
412:
413: return true;
414: }
415:
416: /**
417: * Returns true if the node matches or is a child of a matching element()
418: * scheme XPointer.
419: *
420: * @see org.apache.xerces.xpointer.XPointerProcessor#isFragmentResolved()
421: */
422: public boolean isFragmentResolved() throws XNIException {
423: // Return true if the Fragment was resolved and the current Node depth
424: // is greater than or equal to the depth at which the element was found
425: return fIsFragmentResolved;
426: }
427:
428: /**
429: * Returns true if the XPointer expression resolves to a non-element child
430: * of the current resource fragment.
431: *
432: * @see org.apache.xerces.xpointer.XPointerPart#isChildFragmentResolved()
433: *
434: */
435: public boolean isChildFragmentResolved() {
436: // if only a shorthand pointer was present
437: if (fIsShortHand && fShortHandPointer != null
438: && fChildSequence.length <= 0) {
439: return fShortHandPointer.isChildFragmentResolved();
440: } else {
441: return fWasOnlyEmptyElementFound ? !fWasOnlyEmptyElementFound
442: : (fIsFragmentResolved && (fCurrentChildDepth >= fFoundDepth));
443: }
444: }
445:
446: /**
447: * Reports an XPointer error
448: */
449: protected void reportError(String key, Object[] arguments)
450: throws XNIException {
451: /*fErrorReporter.reportError(XPointerMessageFormatter.XPOINTER_DOMAIN,
452: key, arguments, XMLErrorReporter.SEVERITY_ERROR);
453: */
454: throw new XNIException(
455: (fErrorReporter
456: .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
457: .formatMessage(fErrorReporter.getLocale(), key,
458: arguments));
459: }
460:
461: /**
462: * Initializes error handling objects
463: */
464: protected void initErrorReporter() {
465: if (fErrorReporter == null) {
466: fErrorReporter = new XMLErrorReporter();
467: }
468: if (fErrorHandler == null) {
469: fErrorHandler = new XPointerErrorHandler();
470: }
471: fErrorReporter.putMessageFormatter(
472: XPointerMessageFormatter.XPOINTER_DOMAIN,
473: new XPointerMessageFormatter());
474: }
475:
476: /**
477: * Initializes the element scheme processor
478: */
479: protected void init() {
480: fSchemeName = null;
481: fSchemeData = null;
482: fShortHandPointerName = null;
483: fIsResolveElement = false;
484: fIsElementFound = false;
485: fWasOnlyEmptyElementFound = false;
486: fFoundDepth = 0;
487: fCurrentChildPosition = 1;
488: fCurrentChildDepth = 0;
489: fIsFragmentResolved = false;
490: fShortHandPointer = null;
491:
492: initErrorReporter();
493: }
494:
495: // ************************************************************************
496: // element() Scheme expression scanner
497: // ************************************************************************
498:
499: /**
500: * List of XPointer Framework tokens.
501: *
502: * @xerces.internal
503: *
504: * @author Neil Delima, IBM
505: * @version $Id: ElementSchemePointer.java 447248 2006-09-18 05:25:21Z mrglavas $
506: *
507: */
508: private final class Tokens {
509:
510: /**
511: * XPointer element() scheme
512: * [1] ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence
513: * [2] ChildSequence ::= ('/' [1-9] [0-9]*)+
514: */
515: private static final int XPTRTOKEN_ELEM_NCNAME = 0;
516:
517: private static final int XPTRTOKEN_ELEM_CHILD = 1;
518:
519: // Token names
520: private final String[] fgTokenNames = {
521: "XPTRTOKEN_ELEM_NCNAME", "XPTRTOKEN_ELEM_CHILD" };
522:
523: // Token count
524: private static final int INITIAL_TOKEN_COUNT = 1 << 8;
525:
526: private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
527:
528: private int fTokenCount = 0;
529:
530: // Current token position
531: private int fCurrentTokenIndex;
532:
533: private SymbolTable fSymbolTable;
534:
535: private Hashtable fTokenNames = new Hashtable();
536:
537: /**
538: * Constructor
539: *
540: * @param symbolTable SymbolTable
541: */
542: private Tokens(SymbolTable symbolTable) {
543: fSymbolTable = symbolTable;
544:
545: fTokenNames.put(new Integer(XPTRTOKEN_ELEM_NCNAME),
546: "XPTRTOKEN_ELEM_NCNAME");
547: fTokenNames.put(new Integer(XPTRTOKEN_ELEM_CHILD),
548: "XPTRTOKEN_ELEM_CHILD");
549: }
550:
551: /*
552: * Returns the token String
553: * @param token The index of the token
554: * @return String The token string
555: */
556: private String getTokenString(int token) {
557: return (String) fTokenNames.get(new Integer(token));
558: }
559:
560: /**
561: * Returns the token String
562: * @param token The index of the token
563: * @return String The token string
564: */
565: private Integer getToken(int token) {
566: return (Integer) fTokenNames.get(new Integer(token));
567: }
568:
569: /**
570: * Add the specified string as a token
571: *
572: * @param token The token string
573: */
574: private void addToken(String tokenStr) {
575: Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
576: if (tokenInt == null) {
577: tokenInt = new Integer(fTokenNames.size());
578: fTokenNames.put(tokenInt, tokenStr);
579: }
580: addToken(tokenInt.intValue());
581: }
582:
583: /**
584: * Add the specified int token
585: *
586: * @param token The int specifying the token
587: */
588: private void addToken(int token) {
589: try {
590: fTokens[fTokenCount] = token;
591: } catch (ArrayIndexOutOfBoundsException ex) {
592: int[] oldList = fTokens;
593: fTokens = new int[fTokenCount << 1];
594: System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
595: fTokens[fTokenCount] = token;
596: }
597: fTokenCount++;
598: }
599:
600: /**
601: * Resets the current position to the head of the token list.
602: */
603: private void rewind() {
604: fCurrentTokenIndex = 0;
605: }
606:
607: /**
608: * Returns true if the {@link #getNextToken()} method
609: * returns a valid token.
610: */
611: private boolean hasMore() {
612: return fCurrentTokenIndex < fTokenCount;
613: }
614:
615: /**
616: * Obtains the token at the current position, then advance
617: * the current position by one.
618: *
619: * If there's no such next token, this method throws
620: * <tt>new XNIException("InvalidXPointerExpression");</tt>.
621: */
622: private int nextToken() throws XNIException {
623: if (fCurrentTokenIndex == fTokenCount)
624: reportError("XPointerElementSchemeProcessingError",
625: null);
626: return fTokens[fCurrentTokenIndex++];
627: }
628:
629: /**
630: * Obtains the token at the current position, without advancing
631: * the current position.
632: *
633: * If there's no such next token, this method throws
634: * <tt>new XNIException("InvalidXPointerExpression");</tt>.
635: */
636: private int peekToken() throws XNIException {
637: if (fCurrentTokenIndex == fTokenCount)
638: reportError("XPointerElementSchemeProcessingError",
639: null);
640: return fTokens[fCurrentTokenIndex];
641: }
642:
643: /**
644: * Obtains the token at the current position as a String.
645: *
646: * If there's no current token or if the current token
647: * is not a string token, this method throws
648: * If there's no such next token, this method throws
649: * <tt>new XNIException("InvalidXPointerExpression");</tt>.
650: */
651: private String nextTokenAsString() throws XNIException {
652: String s = getTokenString(nextToken());
653: if (s == null)
654: reportError("XPointerElementSchemeProcessingError",
655: null);
656: return s;
657: }
658:
659: /**
660: * Returns the number of tokens.
661: *
662: */
663: private int getTokenCount() {
664: return fTokenCount;
665: }
666: }
667:
668: /**
669: *
670: * The XPointer expression scanner. Scans the XPointer framework expression.
671: *
672: * @xerces.internal
673: *
674: * @version $Id: ElementSchemePointer.java 447248 2006-09-18 05:25:21Z mrglavas $
675: */
676: private class Scanner {
677:
678: /**
679: * 7-bit ASCII subset
680: *
681: * 0 1 2 3 4 5 6 7 8 9 A B C D E F
682: * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
683: * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
684: * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
685: * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
686: * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
687: * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
688: * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
689: * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
690: */
691: private static final byte CHARTYPE_INVALID = 0, // invalid XML characters, control characters and 7F
692: CHARTYPE_OTHER = 1, // A valid XML character (possibly invalid NCNameChar) that does not fall in one of the other categories
693: CHARTYPE_MINUS = 2, // '-' (0x2D)
694: CHARTYPE_PERIOD = 3, // '.' (0x2E)
695: CHARTYPE_SLASH = 4, // '/' (0x2F)
696: CHARTYPE_DIGIT = 5, // '0'-'9' (0x30 to 0x39)
697: CHARTYPE_LETTER = 6, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
698: CHARTYPE_UNDERSCORE = 7, // '_' (0x5F)
699: CHARTYPE_NONASCII = 8; // Non-ASCII Unicode codepoint (>= 0x80)
700:
701: private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0,
702: 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
703: 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
704: 1, 2, 2, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1,
705: 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
706: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 7, 1, 6,
707: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
708: 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1 };
709:
710: /**
711: * Symbol literals
712: */
713:
714: //
715: // Data
716: //
717: /** Symbol table. */
718: private SymbolTable fSymbolTable;
719:
720: //
721: // Constructors
722: //
723:
724: /**
725: * Constructs an XPath expression scanner.
726: *
727: * @param symbolTable SymbolTable
728: */
729: private Scanner(SymbolTable symbolTable) {
730: // save pool and tokens
731: fSymbolTable = symbolTable;
732:
733: } // <init>(SymbolTable)
734:
735: /**
736: * Scans the XPointer Expression
737: *
738: */
739: private boolean scanExpr(SymbolTable symbolTable,
740: Tokens tokens, String data, int currentOffset,
741: int endOffset) throws XNIException {
742:
743: int ch;
744: int nameOffset;
745: String nameHandle = null;
746:
747: while (true) {
748: if (currentOffset == endOffset) {
749: break;
750: }
751:
752: ch = data.charAt(currentOffset);
753: byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
754: : fASCIICharMap[ch];
755:
756: //
757: // [1] ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence
758: // [2] ChildSequence ::= ('/' [1-9] [0-9]*)+
759: //
760:
761: switch (chartype) {
762:
763: case CHARTYPE_SLASH:
764: // if last character is '/', break and report an error
765: if (++currentOffset == endOffset) {
766: return false;
767: }
768:
769: addToken(tokens, Tokens.XPTRTOKEN_ELEM_CHILD);
770: ch = data.charAt(currentOffset);
771:
772: // ChildSequence ::= ('/' [1-9] [0-9]*)+
773: int child = 0;
774: while (ch >= '0' && ch <= '9') {
775: child = (child * 10) + (ch - '0');
776: if (++currentOffset == endOffset) {
777: break;
778: }
779: ch = data.charAt(currentOffset);
780: }
781:
782: // An invalid child sequence character
783: if (child == 0) {
784: reportError(
785: "InvalidChildSequenceCharacter",
786: new Object[] { new Character((char) ch) });
787: return false;
788: }
789:
790: tokens.addToken(child);
791:
792: break;
793:
794: case CHARTYPE_DIGIT:
795: case CHARTYPE_LETTER:
796: case CHARTYPE_MINUS:
797: case CHARTYPE_NONASCII:
798: case CHARTYPE_OTHER:
799: case CHARTYPE_PERIOD:
800: case CHARTYPE_UNDERSCORE:
801: // Scan the ShortHand Pointer NCName
802: nameOffset = currentOffset;
803: currentOffset = scanNCName(data, endOffset,
804: currentOffset);
805:
806: if (currentOffset == nameOffset) {
807: //return false;
808: reportError("InvalidNCNameInElementSchemeData",
809: new Object[] { data });
810: return false;
811: }
812:
813: if (currentOffset < endOffset) {
814: ch = data.charAt(currentOffset);
815: } else {
816: ch = -1;
817: }
818:
819: nameHandle = symbolTable.addSymbol(data.substring(
820: nameOffset, currentOffset));
821: addToken(tokens, Tokens.XPTRTOKEN_ELEM_NCNAME);
822: tokens.addToken(nameHandle);
823:
824: break;
825: }
826: }
827: return true;
828: }
829:
830: /**
831: * Scans a NCName.
832: * From Namespaces in XML
833: * [5] NCName ::= (Letter | '_') (NCNameChar)*
834: * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
835: *
836: * @param data A String containing the XPointer expression
837: * @param endOffset The int XPointer expression length
838: * @param currentOffset An int representing the current position of the XPointer expression pointer
839: */
840: private int scanNCName(String data, int endOffset,
841: int currentOffset) {
842: int ch = data.charAt(currentOffset);
843: if (ch >= 0x80) {
844: if (!XMLChar.isNameStart(ch)) {
845: return currentOffset;
846: }
847: } else {
848: byte chartype = fASCIICharMap[ch];
849: if (chartype != CHARTYPE_LETTER
850: && chartype != CHARTYPE_UNDERSCORE) {
851: return currentOffset;
852: }
853: }
854: while (++currentOffset < endOffset) {
855: ch = data.charAt(currentOffset);
856: if (ch >= 0x80) {
857: if (!XMLChar.isName(ch)) {
858: break;
859: }
860: } else {
861: byte chartype = fASCIICharMap[ch];
862: if (chartype != CHARTYPE_LETTER
863: && chartype != CHARTYPE_DIGIT
864: && chartype != CHARTYPE_PERIOD
865: && chartype != CHARTYPE_MINUS
866: && chartype != CHARTYPE_UNDERSCORE) {
867: break;
868: }
869: }
870: }
871: return currentOffset;
872: }
873:
874: //
875: // Protected methods
876: //
877:
878: /**
879: * This method adds the specified token to the token list. By
880: * default, this method allows all tokens. However, subclasses
881: * of the XPathExprScanner can override this method in order
882: * to disallow certain tokens from being used in the scanned
883: * XPath expression. This is a convenient way of allowing only
884: * a subset of XPath.
885: */
886: protected void addToken(Tokens tokens, int token)
887: throws XNIException {
888: tokens.addToken(token);
889: } // addToken(int)
890:
891: } // class Scanner
892:
893: }
|