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