Source Code Cross Referenced for XPathParser.java in  » XML » xalan » org » apache » xpath » compiler » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » XML » xalan » org.apache.xpath.compiler 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 1999-2004 The Apache Software Foundation.
0003:         *
0004:         * Licensed under the Apache License, Version 2.0 (the "License");
0005:         * you may not use this file except in compliance with the License.
0006:         * You may obtain a copy of the License at
0007:         *
0008:         *     http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS,
0012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         * See the License for the specific language governing permissions and
0014:         * limitations under the License.
0015:         */
0016:        /*
0017:         * $Id: XPathParser.java,v 1.33 2005/05/17 21:25:46 jycli Exp $
0018:         */
0019:        package org.apache.xpath.compiler;
0020:
0021:        import javax.xml.transform.ErrorListener;
0022:        import javax.xml.transform.TransformerException;
0023:
0024:        import org.apache.xalan.res.XSLMessages;
0025:        import org.apache.xml.utils.PrefixResolver;
0026:        import org.apache.xpath.XPathProcessorException;
0027:        import org.apache.xpath.domapi.XPathStylesheetDOM3Exception;
0028:        import org.apache.xpath.objects.XNumber;
0029:        import org.apache.xpath.objects.XString;
0030:        import org.apache.xpath.res.XPATHErrorResources;
0031:
0032:        /**
0033:         * Tokenizes and parses XPath expressions. This should really be named
0034:         * XPathParserImpl, and may be renamed in the future.
0035:         * @xsl.usage general
0036:         */
0037:        public class XPathParser {
0038:            // %REVIEW% Is there a better way of doing this?
0039:            // Upside is minimum object churn. Downside is that we don't have a useful
0040:            // backtrace in the exception itself -- but we don't expect to need one.
0041:            static public final String CONTINUE_AFTER_FATAL_ERROR = "CONTINUE_AFTER_FATAL_ERROR";
0042:
0043:            /**
0044:             * The XPath to be processed.
0045:             */
0046:            private OpMap m_ops;
0047:
0048:            /**
0049:             * The next token in the pattern.
0050:             */
0051:            transient String m_token;
0052:
0053:            /**
0054:             * The first char in m_token, the theory being that this
0055:             * is an optimization because we won't have to do charAt(0) as
0056:             * often.
0057:             */
0058:            transient char m_tokenChar = 0;
0059:
0060:            /**
0061:             * The position in the token queue is tracked by m_queueMark.
0062:             */
0063:            int m_queueMark = 0;
0064:
0065:            /**
0066:             * Results from checking FilterExpr syntax
0067:             */
0068:            protected final static int FILTER_MATCH_FAILED = 0;
0069:            protected final static int FILTER_MATCH_PRIMARY = 1;
0070:            protected final static int FILTER_MATCH_PREDICATES = 2;
0071:
0072:            /**
0073:             * The parser constructor.
0074:             */
0075:            public XPathParser(ErrorListener errorListener,
0076:                    javax.xml.transform.SourceLocator sourceLocator) {
0077:                m_errorListener = errorListener;
0078:                m_sourceLocator = sourceLocator;
0079:            }
0080:
0081:            /**
0082:             * The prefix resolver to map prefixes to namespaces in the OpMap.
0083:             */
0084:            PrefixResolver m_namespaceContext;
0085:
0086:            /**
0087:             * Given an string, init an XPath object for selections,
0088:             * in order that a parse doesn't
0089:             * have to be done each time the expression is evaluated.
0090:             * 
0091:             * @param compiler The compiler object.
0092:             * @param expression A string conforming to the XPath grammar.
0093:             * @param namespaceContext An object that is able to resolve prefixes in
0094:             * the XPath to namespaces.
0095:             *
0096:             * @throws javax.xml.transform.TransformerException
0097:             */
0098:            public void initXPath(Compiler compiler, String expression,
0099:                    PrefixResolver namespaceContext)
0100:                    throws javax.xml.transform.TransformerException {
0101:
0102:                m_ops = compiler;
0103:                m_namespaceContext = namespaceContext;
0104:                m_functionTable = compiler.getFunctionTable();
0105:
0106:                Lexer lexer = new Lexer(compiler, namespaceContext, this );
0107:
0108:                lexer.tokenize(expression);
0109:
0110:                m_ops.setOp(0, OpCodes.OP_XPATH);
0111:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, 2);
0112:
0113:                // Patch for Christine's gripe. She wants her errorHandler to return from
0114:                // a fatal error and continue trying to parse, rather than throwing an exception.
0115:                // Without the patch, that put us into an endless loop.
0116:                //
0117:                // %REVIEW% Is there a better way of doing this?
0118:                // %REVIEW% Are there any other cases which need the safety net?
0119:                // 	(and if so do we care right now, or should we rewrite the XPath
0120:                //	grammar engine and can fix it at that time?)
0121:                try {
0122:
0123:                    nextToken();
0124:                    Expr();
0125:
0126:                    if (null != m_token) {
0127:                        String extraTokens = "";
0128:
0129:                        while (null != m_token) {
0130:                            extraTokens += "'" + m_token + "'";
0131:
0132:                            nextToken();
0133:
0134:                            if (null != m_token)
0135:                                extraTokens += ", ";
0136:                        }
0137:
0138:                        error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
0139:                                new Object[] { extraTokens }); //"Extra illegal tokens: "+extraTokens);
0140:                    }
0141:
0142:                } catch (org.apache.xpath.XPathProcessorException e) {
0143:                    if (CONTINUE_AFTER_FATAL_ERROR.equals(e.getMessage())) {
0144:                        // What I _want_ to do is null out this XPath.
0145:                        // I doubt this has the desired effect, but I'm not sure what else to do.
0146:                        // %REVIEW%!!!
0147:                        initXPath(compiler, "/..", namespaceContext);
0148:                    } else
0149:                        throw e;
0150:                }
0151:
0152:                compiler.shrink();
0153:            }
0154:
0155:            /**
0156:             * Given an string, init an XPath object for pattern matches,
0157:             * in order that a parse doesn't
0158:             * have to be done each time the expression is evaluated.
0159:             * @param compiler The XPath object to be initialized.
0160:             * @param expression A String representing the XPath.
0161:             * @param namespaceContext An object that is able to resolve prefixes in
0162:             * the XPath to namespaces.
0163:             *
0164:             * @throws javax.xml.transform.TransformerException
0165:             */
0166:            public void initMatchPattern(Compiler compiler, String expression,
0167:                    PrefixResolver namespaceContext)
0168:                    throws javax.xml.transform.TransformerException {
0169:
0170:                m_ops = compiler;
0171:                m_namespaceContext = namespaceContext;
0172:                m_functionTable = compiler.getFunctionTable();
0173:
0174:                Lexer lexer = new Lexer(compiler, namespaceContext, this );
0175:
0176:                lexer.tokenize(expression);
0177:
0178:                m_ops.setOp(0, OpCodes.OP_MATCHPATTERN);
0179:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, 2);
0180:
0181:                nextToken();
0182:                Pattern();
0183:
0184:                if (null != m_token) {
0185:                    String extraTokens = "";
0186:
0187:                    while (null != m_token) {
0188:                        extraTokens += "'" + m_token + "'";
0189:
0190:                        nextToken();
0191:
0192:                        if (null != m_token)
0193:                            extraTokens += ", ";
0194:                    }
0195:
0196:                    error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
0197:                            new Object[] { extraTokens }); //"Extra illegal tokens: "+extraTokens);
0198:                }
0199:
0200:                // Terminate for safety.
0201:                m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
0202:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
0203:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
0204:
0205:                m_ops.shrink();
0206:            }
0207:
0208:            /** The error listener where syntax errors are to be sent.
0209:             */
0210:            private ErrorListener m_errorListener;
0211:
0212:            /** The source location of the XPath. */
0213:            javax.xml.transform.SourceLocator m_sourceLocator;
0214:
0215:            /** The table contains build-in functions and customized functions */
0216:            private FunctionTable m_functionTable;
0217:
0218:            /**
0219:             * Allow an application to register an error event handler, where syntax 
0220:             * errors will be sent.  If the error listener is not set, syntax errors 
0221:             * will be sent to System.err.
0222:             * 
0223:             * @param handler Reference to error listener where syntax errors will be 
0224:             *                sent.
0225:             */
0226:            public void setErrorHandler(ErrorListener handler) {
0227:                m_errorListener = handler;
0228:            }
0229:
0230:            /**
0231:             * Return the current error listener.
0232:             *
0233:             * @return The error listener, which should not normally be null, but may be.
0234:             */
0235:            public ErrorListener getErrorListener() {
0236:                return m_errorListener;
0237:            }
0238:
0239:            /**
0240:             * Check whether m_token matches the target string. 
0241:             *
0242:             * @param s A string reference or null.
0243:             *
0244:             * @return If m_token is null, returns false (or true if s is also null), or 
0245:             * return true if the current token matches the string, else false.
0246:             */
0247:            final boolean tokenIs(String s) {
0248:                return (m_token != null) ? (m_token.equals(s)) : (s == null);
0249:            }
0250:
0251:            /**
0252:             * Check whether m_tokenChar==c. 
0253:             *
0254:             * @param c A character to be tested.
0255:             *
0256:             * @return If m_token is null, returns false, or return true if c matches 
0257:             *         the current token.
0258:             */
0259:            final boolean tokenIs(char c) {
0260:                return (m_token != null) ? (m_tokenChar == c) : false;
0261:            }
0262:
0263:            /**
0264:             * Look ahead of the current token in order to
0265:             * make a branching decision.
0266:             *
0267:             * @param c the character to be tested for.
0268:             * @param n number of tokens to look ahead.  Must be
0269:             * greater than 1.
0270:             *
0271:             * @return true if the next token matches the character argument.
0272:             */
0273:            final boolean lookahead(char c, int n) {
0274:
0275:                int pos = (m_queueMark + n);
0276:                boolean b;
0277:
0278:                if ((pos <= m_ops.getTokenQueueSize()) && (pos > 0)
0279:                        && (m_ops.getTokenQueueSize() != 0)) {
0280:                    String tok = ((String) m_ops.m_tokenQueue
0281:                            .elementAt(pos - 1));
0282:
0283:                    b = (tok.length() == 1) ? (tok.charAt(0) == c) : false;
0284:                } else {
0285:                    b = false;
0286:                }
0287:
0288:                return b;
0289:            }
0290:
0291:            /**
0292:             * Look behind the first character of the current token in order to
0293:             * make a branching decision.
0294:             * 
0295:             * @param c the character to compare it to.
0296:             * @param n number of tokens to look behind.  Must be
0297:             * greater than 1.  Note that the look behind terminates
0298:             * at either the beginning of the string or on a '|'
0299:             * character.  Because of this, this method should only
0300:             * be used for pattern matching.
0301:             *
0302:             * @return true if the token behind the current token matches the character 
0303:             *         argument.
0304:             */
0305:            private final boolean lookbehind(char c, int n) {
0306:
0307:                boolean isToken;
0308:                int lookBehindPos = m_queueMark - (n + 1);
0309:
0310:                if (lookBehindPos >= 0) {
0311:                    String lookbehind = (String) m_ops.m_tokenQueue
0312:                            .elementAt(lookBehindPos);
0313:
0314:                    if (lookbehind.length() == 1) {
0315:                        char c0 = (lookbehind == null) ? '|' : lookbehind
0316:                                .charAt(0);
0317:
0318:                        isToken = (c0 == '|') ? false : (c0 == c);
0319:                    } else {
0320:                        isToken = false;
0321:                    }
0322:                } else {
0323:                    isToken = false;
0324:                }
0325:
0326:                return isToken;
0327:            }
0328:
0329:            /**
0330:             * look behind the current token in order to
0331:             * see if there is a useable token.
0332:             * 
0333:             * @param n number of tokens to look behind.  Must be
0334:             * greater than 1.  Note that the look behind terminates
0335:             * at either the beginning of the string or on a '|'
0336:             * character.  Because of this, this method should only
0337:             * be used for pattern matching.
0338:             * 
0339:             * @return true if look behind has a token, false otherwise.
0340:             */
0341:            private final boolean lookbehindHasToken(int n) {
0342:
0343:                boolean hasToken;
0344:
0345:                if ((m_queueMark - n) > 0) {
0346:                    String lookbehind = (String) m_ops.m_tokenQueue
0347:                            .elementAt(m_queueMark - (n - 1));
0348:                    char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
0349:
0350:                    hasToken = (c0 == '|') ? false : true;
0351:                } else {
0352:                    hasToken = false;
0353:                }
0354:
0355:                return hasToken;
0356:            }
0357:
0358:            /**
0359:             * Look ahead of the current token in order to
0360:             * make a branching decision.
0361:             * 
0362:             * @param s the string to compare it to.
0363:             * @param n number of tokens to lookahead.  Must be
0364:             * greater than 1.
0365:             *
0366:             * @return true if the token behind the current token matches the string 
0367:             *         argument.
0368:             */
0369:            private final boolean lookahead(String s, int n) {
0370:
0371:                boolean isToken;
0372:
0373:                if ((m_queueMark + n) <= m_ops.getTokenQueueSize()) {
0374:                    String lookahead = (String) m_ops.m_tokenQueue
0375:                            .elementAt(m_queueMark + (n - 1));
0376:
0377:                    isToken = (lookahead != null) ? lookahead.equals(s)
0378:                            : (s == null);
0379:                } else {
0380:                    isToken = (null == s);
0381:                }
0382:
0383:                return isToken;
0384:            }
0385:
0386:            /**
0387:             * Retrieve the next token from the command and
0388:             * store it in m_token string.
0389:             */
0390:            private final void nextToken() {
0391:
0392:                if (m_queueMark < m_ops.getTokenQueueSize()) {
0393:                    m_token = (String) m_ops.m_tokenQueue
0394:                            .elementAt(m_queueMark++);
0395:                    m_tokenChar = m_token.charAt(0);
0396:                } else {
0397:                    m_token = null;
0398:                    m_tokenChar = 0;
0399:                }
0400:            }
0401:
0402:            /**
0403:             * Retrieve a token relative to the current token.
0404:             * 
0405:             * @param i Position relative to current token.
0406:             *
0407:             * @return The string at the given index, or null if the index is out 
0408:             *         of range.
0409:             */
0410:            private final String getTokenRelative(int i) {
0411:
0412:                String tok;
0413:                int relative = m_queueMark + i;
0414:
0415:                if ((relative > 0) && (relative < m_ops.getTokenQueueSize())) {
0416:                    tok = (String) m_ops.m_tokenQueue.elementAt(relative);
0417:                } else {
0418:                    tok = null;
0419:                }
0420:
0421:                return tok;
0422:            }
0423:
0424:            /**
0425:             * Retrieve the previous token from the command and
0426:             * store it in m_token string.
0427:             */
0428:            private final void prevToken() {
0429:
0430:                if (m_queueMark > 0) {
0431:                    m_queueMark--;
0432:
0433:                    m_token = (String) m_ops.m_tokenQueue
0434:                            .elementAt(m_queueMark);
0435:                    m_tokenChar = m_token.charAt(0);
0436:                } else {
0437:                    m_token = null;
0438:                    m_tokenChar = 0;
0439:                }
0440:            }
0441:
0442:            /**
0443:             * Consume an expected token, throwing an exception if it
0444:             * isn't there.
0445:             *
0446:             * @param expected The string to be expected.
0447:             *
0448:             * @throws javax.xml.transform.TransformerException
0449:             */
0450:            private final void consumeExpected(String expected)
0451:                    throws javax.xml.transform.TransformerException {
0452:
0453:                if (tokenIs(expected)) {
0454:                    nextToken();
0455:                } else {
0456:                    error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND,
0457:                            new Object[] { expected, m_token }); //"Expected "+expected+", but found: "+m_token);
0458:
0459:                    // Patch for Christina's gripe. She wants her errorHandler to return from
0460:                    // this error and continue trying to parse, rather than throwing an exception.
0461:                    // Without the patch, that put us into an endless loop.
0462:                    throw new XPathProcessorException(
0463:                            CONTINUE_AFTER_FATAL_ERROR);
0464:                }
0465:            }
0466:
0467:            /**
0468:             * Consume an expected token, throwing an exception if it
0469:             * isn't there.
0470:             *
0471:             * @param expected the character to be expected.
0472:             *
0473:             * @throws javax.xml.transform.TransformerException
0474:             */
0475:            private final void consumeExpected(char expected)
0476:                    throws javax.xml.transform.TransformerException {
0477:
0478:                if (tokenIs(expected)) {
0479:                    nextToken();
0480:                } else {
0481:                    error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND,
0482:                            new Object[] { String.valueOf(expected), m_token }); //"Expected "+expected+", but found: "+m_token);
0483:
0484:                    // Patch for Christina's gripe. She wants her errorHandler to return from
0485:                    // this error and continue trying to parse, rather than throwing an exception.
0486:                    // Without the patch, that put us into an endless loop.
0487:                    throw new XPathProcessorException(
0488:                            CONTINUE_AFTER_FATAL_ERROR);
0489:                }
0490:            }
0491:
0492:            /**
0493:             * Warn the user of a problem.
0494:             *
0495:             * @param msg An error msgkey that corresponds to one of the constants found 
0496:             *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
0497:             *            a key for a format string.
0498:             * @param args An array of arguments represented in the format string, which 
0499:             *             may be null.
0500:             *
0501:             * @throws TransformerException if the current ErrorListoner determines to 
0502:             *                              throw an exception.
0503:             */
0504:            void warn(String msg, Object[] args) throws TransformerException {
0505:
0506:                String fmsg = XSLMessages.createXPATHWarning(msg, args);
0507:                ErrorListener ehandler = this .getErrorListener();
0508:
0509:                if (null != ehandler) {
0510:                    // TO DO: Need to get stylesheet Locator from here.
0511:                    ehandler.warning(new TransformerException(fmsg,
0512:                            m_sourceLocator));
0513:                } else {
0514:                    // Should never happen.
0515:                    System.err.println(fmsg);
0516:                }
0517:            }
0518:
0519:            /**
0520:             * Notify the user of an assertion error, and probably throw an
0521:             * exception.
0522:             *
0523:             * @param b  If false, a runtime exception will be thrown.
0524:             * @param msg The assertion message, which should be informative.
0525:             * 
0526:             * @throws RuntimeException if the b argument is false.
0527:             */
0528:            private void assertion(boolean b, String msg) {
0529:
0530:                if (!b) {
0531:                    String fMsg = XSLMessages
0532:                            .createXPATHMessage(
0533:                                    XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
0534:                                    new Object[] { msg });
0535:
0536:                    throw new RuntimeException(fMsg);
0537:                }
0538:            }
0539:
0540:            /**
0541:             * Notify the user of an error, and probably throw an
0542:             * exception.
0543:             *
0544:             * @param msg An error msgkey that corresponds to one of the constants found 
0545:             *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
0546:             *            a key for a format string.
0547:             * @param args An array of arguments represented in the format string, which 
0548:             *             may be null.
0549:             *
0550:             * @throws TransformerException if the current ErrorListoner determines to 
0551:             *                              throw an exception.
0552:             */
0553:            void error(String msg, Object[] args) throws TransformerException {
0554:
0555:                String fmsg = XSLMessages.createXPATHMessage(msg, args);
0556:                ErrorListener ehandler = this .getErrorListener();
0557:
0558:                TransformerException te = new TransformerException(fmsg,
0559:                        m_sourceLocator);
0560:                if (null != ehandler) {
0561:                    // TO DO: Need to get stylesheet Locator from here.
0562:                    ehandler.fatalError(te);
0563:                } else {
0564:                    // System.err.println(fmsg);
0565:                    throw te;
0566:                }
0567:            }
0568:
0569:            /**
0570:             * This method is added to support DOM 3 XPath API.
0571:             * <p>
0572:             * This method is exactly like error(String, Object[]); except that
0573:             * the underlying TransformerException is 
0574:             * XpathStylesheetDOM3Exception (which extends TransformerException).
0575:             * <p>
0576:             * So older XPath code in Xalan is not affected by this. To older XPath code
0577:             * the behavior of whether error() or errorForDOM3() is called because it is
0578:             * always catching TransformerException objects and is oblivious to
0579:             * the new subclass of XPathStylesheetDOM3Exception. Older XPath code 
0580:             * runs as before.
0581:             * <p>
0582:             * However, newer DOM3 XPath code upon catching a TransformerException can
0583:             * can check if the exception is an instance of XPathStylesheetDOM3Exception
0584:             * and take appropriate action.
0585:             * 
0586:             * @param msg An error msgkey that corresponds to one of the constants found 
0587:             *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is 
0588:             *            a key for a format string.
0589:             * @param args An array of arguments represented in the format string, which 
0590:             *             may be null.
0591:             *
0592:             * @throws TransformerException if the current ErrorListoner determines to 
0593:             *                              throw an exception.
0594:             */
0595:            void errorForDOM3(String msg, Object[] args)
0596:                    throws TransformerException {
0597:
0598:                String fmsg = XSLMessages.createXPATHMessage(msg, args);
0599:                ErrorListener ehandler = this .getErrorListener();
0600:
0601:                TransformerException te = new XPathStylesheetDOM3Exception(
0602:                        fmsg, m_sourceLocator);
0603:                if (null != ehandler) {
0604:                    // TO DO: Need to get stylesheet Locator from here.
0605:                    ehandler.fatalError(te);
0606:                } else {
0607:                    // System.err.println(fmsg);
0608:                    throw te;
0609:                }
0610:            }
0611:
0612:            /**
0613:             * Dump the remaining token queue.
0614:             * Thanks to Craig for this.
0615:             *
0616:             * @return A dump of the remaining token queue, which may be appended to 
0617:             *         an error message.
0618:             */
0619:            protected String dumpRemainingTokenQueue() {
0620:
0621:                int q = m_queueMark;
0622:                String returnMsg;
0623:
0624:                if (q < m_ops.getTokenQueueSize()) {
0625:                    String msg = "\n Remaining tokens: (";
0626:
0627:                    while (q < m_ops.getTokenQueueSize()) {
0628:                        String t = (String) m_ops.m_tokenQueue.elementAt(q++);
0629:
0630:                        msg += (" '" + t + "'");
0631:                    }
0632:
0633:                    returnMsg = msg + ")";
0634:                } else {
0635:                    returnMsg = "";
0636:                }
0637:
0638:                return returnMsg;
0639:            }
0640:
0641:            /**
0642:             * Given a string, return the corresponding function token.
0643:             *
0644:             * @param key A local name of a function.
0645:             *
0646:             * @return   The function ID, which may correspond to one of the FUNC_XXX 
0647:             *    values found in {@link org.apache.xpath.compiler.FunctionTable}, but may 
0648:             *    be a value installed by an external module.
0649:             */
0650:            final int getFunctionToken(String key) {
0651:
0652:                int tok;
0653:
0654:                Object id;
0655:
0656:                try {
0657:                    // These are nodetests, xpathparser treats them as functions when parsing
0658:                    // a FilterExpr. 
0659:                    id = Keywords.lookupNodeTest(key);
0660:                    if (null == id)
0661:                        id = m_functionTable.getFunctionID(key);
0662:                    tok = ((Integer) id).intValue();
0663:                } catch (NullPointerException npe) {
0664:                    tok = -1;
0665:                } catch (ClassCastException cce) {
0666:                    tok = -1;
0667:                }
0668:
0669:                return tok;
0670:            }
0671:
0672:            /**
0673:             * Insert room for operation.  This will NOT set
0674:             * the length value of the operation, but will update
0675:             * the length value for the total expression.
0676:             *
0677:             * @param pos The position where the op is to be inserted.
0678:             * @param length The length of the operation space in the op map.
0679:             * @param op The op code to the inserted.
0680:             */
0681:            void insertOp(int pos, int length, int op) {
0682:
0683:                int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0684:
0685:                for (int i = totalLen - 1; i >= pos; i--) {
0686:                    m_ops.setOp(i + length, m_ops.getOp(i));
0687:                }
0688:
0689:                m_ops.setOp(pos, op);
0690:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, totalLen + length);
0691:            }
0692:
0693:            /**
0694:             * Insert room for operation.  This WILL set
0695:             * the length value of the operation, and will update
0696:             * the length value for the total expression.
0697:             *
0698:             * @param length The length of the operation.
0699:             * @param op The op code to the inserted.
0700:             */
0701:            void appendOp(int length, int op) {
0702:
0703:                int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0704:
0705:                m_ops.setOp(totalLen, op);
0706:                m_ops.setOp(totalLen + OpMap.MAPINDEX_LENGTH, length);
0707:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, totalLen + length);
0708:            }
0709:
0710:            // ============= EXPRESSIONS FUNCTIONS =================
0711:
0712:            /**
0713:             *
0714:             *
0715:             * Expr  ::=  OrExpr
0716:             *
0717:             *
0718:             * @throws javax.xml.transform.TransformerException
0719:             */
0720:            protected void Expr()
0721:                    throws javax.xml.transform.TransformerException {
0722:                OrExpr();
0723:            }
0724:
0725:            /**
0726:             *
0727:             *
0728:             * OrExpr  ::=  AndExpr
0729:             * | OrExpr 'or' AndExpr
0730:             *
0731:             *
0732:             * @throws javax.xml.transform.TransformerException
0733:             */
0734:            protected void OrExpr()
0735:                    throws javax.xml.transform.TransformerException {
0736:
0737:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0738:
0739:                AndExpr();
0740:
0741:                if ((null != m_token) && tokenIs("or")) {
0742:                    nextToken();
0743:                    insertOp(opPos, 2, OpCodes.OP_OR);
0744:                    OrExpr();
0745:
0746:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
0747:                            .getOp(OpMap.MAPINDEX_LENGTH)
0748:                            - opPos);
0749:                }
0750:            }
0751:
0752:            /**
0753:             *
0754:             *
0755:             * AndExpr  ::=  EqualityExpr
0756:             * | AndExpr 'and' EqualityExpr
0757:             *
0758:             *
0759:             * @throws javax.xml.transform.TransformerException
0760:             */
0761:            protected void AndExpr()
0762:                    throws javax.xml.transform.TransformerException {
0763:
0764:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0765:
0766:                EqualityExpr(-1);
0767:
0768:                if ((null != m_token) && tokenIs("and")) {
0769:                    nextToken();
0770:                    insertOp(opPos, 2, OpCodes.OP_AND);
0771:                    AndExpr();
0772:
0773:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
0774:                            .getOp(OpMap.MAPINDEX_LENGTH)
0775:                            - opPos);
0776:                }
0777:            }
0778:
0779:            /**
0780:             *
0781:             * @returns an Object which is either a String, a Number, a Boolean, or a vector
0782:             * of nodes.
0783:             *
0784:             * EqualityExpr  ::=  RelationalExpr
0785:             * | EqualityExpr '=' RelationalExpr
0786:             *
0787:             *
0788:             * @param addPos Position where expression is to be added, or -1 for append.
0789:             *
0790:             * @return the position at the end of the equality expression.
0791:             *
0792:             * @throws javax.xml.transform.TransformerException
0793:             */
0794:            protected int EqualityExpr(int addPos)
0795:                    throws javax.xml.transform.TransformerException {
0796:
0797:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0798:
0799:                if (-1 == addPos)
0800:                    addPos = opPos;
0801:
0802:                RelationalExpr(-1);
0803:
0804:                if (null != m_token) {
0805:                    if (tokenIs('!') && lookahead('=', 1)) {
0806:                        nextToken();
0807:                        nextToken();
0808:                        insertOp(addPos, 2, OpCodes.OP_NOTEQUALS);
0809:
0810:                        int opPlusLeftHandLen = m_ops
0811:                                .getOp(OpMap.MAPINDEX_LENGTH)
0812:                                - addPos;
0813:
0814:                        addPos = EqualityExpr(addPos);
0815:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0816:                                .getOp(addPos + opPlusLeftHandLen + 1)
0817:                                + opPlusLeftHandLen);
0818:                        addPos += 2;
0819:                    } else if (tokenIs('=')) {
0820:                        nextToken();
0821:                        insertOp(addPos, 2, OpCodes.OP_EQUALS);
0822:
0823:                        int opPlusLeftHandLen = m_ops
0824:                                .getOp(OpMap.MAPINDEX_LENGTH)
0825:                                - addPos;
0826:
0827:                        addPos = EqualityExpr(addPos);
0828:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0829:                                .getOp(addPos + opPlusLeftHandLen + 1)
0830:                                + opPlusLeftHandLen);
0831:                        addPos += 2;
0832:                    }
0833:                }
0834:
0835:                return addPos;
0836:            }
0837:
0838:            /**
0839:             * .
0840:             * @returns an Object which is either a String, a Number, a Boolean, or a vector
0841:             * of nodes.
0842:             *
0843:             * RelationalExpr  ::=  AdditiveExpr
0844:             * | RelationalExpr '<' AdditiveExpr
0845:             * | RelationalExpr '>' AdditiveExpr
0846:             * | RelationalExpr '<=' AdditiveExpr
0847:             * | RelationalExpr '>=' AdditiveExpr
0848:             *
0849:             *
0850:             * @param addPos Position where expression is to be added, or -1 for append.
0851:             *
0852:             * @return the position at the end of the relational expression.
0853:             *
0854:             * @throws javax.xml.transform.TransformerException
0855:             */
0856:            protected int RelationalExpr(int addPos)
0857:                    throws javax.xml.transform.TransformerException {
0858:
0859:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0860:
0861:                if (-1 == addPos)
0862:                    addPos = opPos;
0863:
0864:                AdditiveExpr(-1);
0865:
0866:                if (null != m_token) {
0867:                    if (tokenIs('<')) {
0868:                        nextToken();
0869:
0870:                        if (tokenIs('=')) {
0871:                            nextToken();
0872:                            insertOp(addPos, 2, OpCodes.OP_LTE);
0873:                        } else {
0874:                            insertOp(addPos, 2, OpCodes.OP_LT);
0875:                        }
0876:
0877:                        int opPlusLeftHandLen = m_ops
0878:                                .getOp(OpMap.MAPINDEX_LENGTH)
0879:                                - addPos;
0880:
0881:                        addPos = RelationalExpr(addPos);
0882:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0883:                                .getOp(addPos + opPlusLeftHandLen + 1)
0884:                                + opPlusLeftHandLen);
0885:                        addPos += 2;
0886:                    } else if (tokenIs('>')) {
0887:                        nextToken();
0888:
0889:                        if (tokenIs('=')) {
0890:                            nextToken();
0891:                            insertOp(addPos, 2, OpCodes.OP_GTE);
0892:                        } else {
0893:                            insertOp(addPos, 2, OpCodes.OP_GT);
0894:                        }
0895:
0896:                        int opPlusLeftHandLen = m_ops
0897:                                .getOp(OpMap.MAPINDEX_LENGTH)
0898:                                - addPos;
0899:
0900:                        addPos = RelationalExpr(addPos);
0901:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0902:                                .getOp(addPos + opPlusLeftHandLen + 1)
0903:                                + opPlusLeftHandLen);
0904:                        addPos += 2;
0905:                    }
0906:                }
0907:
0908:                return addPos;
0909:            }
0910:
0911:            /**
0912:             * This has to handle construction of the operations so that they are evaluated
0913:             * in pre-fix order.  So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
0914:             * evaluated as |-|+|9|7|6|.
0915:             *
0916:             * AdditiveExpr  ::=  MultiplicativeExpr
0917:             * | AdditiveExpr '+' MultiplicativeExpr
0918:             * | AdditiveExpr '-' MultiplicativeExpr
0919:             *
0920:             *
0921:             * @param addPos Position where expression is to be added, or -1 for append.
0922:             *
0923:             * @return the position at the end of the equality expression.
0924:             *
0925:             * @throws javax.xml.transform.TransformerException
0926:             */
0927:            protected int AdditiveExpr(int addPos)
0928:                    throws javax.xml.transform.TransformerException {
0929:
0930:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0931:
0932:                if (-1 == addPos)
0933:                    addPos = opPos;
0934:
0935:                MultiplicativeExpr(-1);
0936:
0937:                if (null != m_token) {
0938:                    if (tokenIs('+')) {
0939:                        nextToken();
0940:                        insertOp(addPos, 2, OpCodes.OP_PLUS);
0941:
0942:                        int opPlusLeftHandLen = m_ops
0943:                                .getOp(OpMap.MAPINDEX_LENGTH)
0944:                                - addPos;
0945:
0946:                        addPos = AdditiveExpr(addPos);
0947:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0948:                                .getOp(addPos + opPlusLeftHandLen + 1)
0949:                                + opPlusLeftHandLen);
0950:                        addPos += 2;
0951:                    } else if (tokenIs('-')) {
0952:                        nextToken();
0953:                        insertOp(addPos, 2, OpCodes.OP_MINUS);
0954:
0955:                        int opPlusLeftHandLen = m_ops
0956:                                .getOp(OpMap.MAPINDEX_LENGTH)
0957:                                - addPos;
0958:
0959:                        addPos = AdditiveExpr(addPos);
0960:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
0961:                                .getOp(addPos + opPlusLeftHandLen + 1)
0962:                                + opPlusLeftHandLen);
0963:                        addPos += 2;
0964:                    }
0965:                }
0966:
0967:                return addPos;
0968:            }
0969:
0970:            /**
0971:             * This has to handle construction of the operations so that they are evaluated
0972:             * in pre-fix order.  So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
0973:             * evaluated as |-|+|9|7|6|.
0974:             *
0975:             * MultiplicativeExpr  ::=  UnaryExpr
0976:             * | MultiplicativeExpr MultiplyOperator UnaryExpr
0977:             * | MultiplicativeExpr 'div' UnaryExpr
0978:             * | MultiplicativeExpr 'mod' UnaryExpr
0979:             * | MultiplicativeExpr 'quo' UnaryExpr
0980:             *
0981:             * @param addPos Position where expression is to be added, or -1 for append.
0982:             *
0983:             * @return the position at the end of the equality expression.
0984:             *
0985:             * @throws javax.xml.transform.TransformerException
0986:             */
0987:            protected int MultiplicativeExpr(int addPos)
0988:                    throws javax.xml.transform.TransformerException {
0989:
0990:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
0991:
0992:                if (-1 == addPos)
0993:                    addPos = opPos;
0994:
0995:                UnaryExpr();
0996:
0997:                if (null != m_token) {
0998:                    if (tokenIs('*')) {
0999:                        nextToken();
1000:                        insertOp(addPos, 2, OpCodes.OP_MULT);
1001:
1002:                        int opPlusLeftHandLen = m_ops
1003:                                .getOp(OpMap.MAPINDEX_LENGTH)
1004:                                - addPos;
1005:
1006:                        addPos = MultiplicativeExpr(addPos);
1007:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
1008:                                .getOp(addPos + opPlusLeftHandLen + 1)
1009:                                + opPlusLeftHandLen);
1010:                        addPos += 2;
1011:                    } else if (tokenIs("div")) {
1012:                        nextToken();
1013:                        insertOp(addPos, 2, OpCodes.OP_DIV);
1014:
1015:                        int opPlusLeftHandLen = m_ops
1016:                                .getOp(OpMap.MAPINDEX_LENGTH)
1017:                                - addPos;
1018:
1019:                        addPos = MultiplicativeExpr(addPos);
1020:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
1021:                                .getOp(addPos + opPlusLeftHandLen + 1)
1022:                                + opPlusLeftHandLen);
1023:                        addPos += 2;
1024:                    } else if (tokenIs("mod")) {
1025:                        nextToken();
1026:                        insertOp(addPos, 2, OpCodes.OP_MOD);
1027:
1028:                        int opPlusLeftHandLen = m_ops
1029:                                .getOp(OpMap.MAPINDEX_LENGTH)
1030:                                - addPos;
1031:
1032:                        addPos = MultiplicativeExpr(addPos);
1033:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
1034:                                .getOp(addPos + opPlusLeftHandLen + 1)
1035:                                + opPlusLeftHandLen);
1036:                        addPos += 2;
1037:                    } else if (tokenIs("quo")) {
1038:                        nextToken();
1039:                        insertOp(addPos, 2, OpCodes.OP_QUO);
1040:
1041:                        int opPlusLeftHandLen = m_ops
1042:                                .getOp(OpMap.MAPINDEX_LENGTH)
1043:                                - addPos;
1044:
1045:                        addPos = MultiplicativeExpr(addPos);
1046:                        m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH, m_ops
1047:                                .getOp(addPos + opPlusLeftHandLen + 1)
1048:                                + opPlusLeftHandLen);
1049:                        addPos += 2;
1050:                    }
1051:                }
1052:
1053:                return addPos;
1054:            }
1055:
1056:            /**
1057:             *
1058:             * UnaryExpr  ::=  UnionExpr
1059:             * | '-' UnaryExpr
1060:             *
1061:             *
1062:             * @throws javax.xml.transform.TransformerException
1063:             */
1064:            protected void UnaryExpr()
1065:                    throws javax.xml.transform.TransformerException {
1066:
1067:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1068:                boolean isNeg = false;
1069:
1070:                if (m_tokenChar == '-') {
1071:                    nextToken();
1072:                    appendOp(2, OpCodes.OP_NEG);
1073:
1074:                    isNeg = true;
1075:                }
1076:
1077:                UnionExpr();
1078:
1079:                if (isNeg)
1080:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1081:                            .getOp(OpMap.MAPINDEX_LENGTH)
1082:                            - opPos);
1083:            }
1084:
1085:            /**
1086:             *
1087:             * StringExpr  ::=  Expr
1088:             *
1089:             *
1090:             * @throws javax.xml.transform.TransformerException
1091:             */
1092:            protected void StringExpr()
1093:                    throws javax.xml.transform.TransformerException {
1094:
1095:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1096:
1097:                appendOp(2, OpCodes.OP_STRING);
1098:                Expr();
1099:
1100:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1101:                        .getOp(OpMap.MAPINDEX_LENGTH)
1102:                        - opPos);
1103:            }
1104:
1105:            /**
1106:             *
1107:             *
1108:             * StringExpr  ::=  Expr
1109:             *
1110:             *
1111:             * @throws javax.xml.transform.TransformerException
1112:             */
1113:            protected void BooleanExpr()
1114:                    throws javax.xml.transform.TransformerException {
1115:
1116:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1117:
1118:                appendOp(2, OpCodes.OP_BOOL);
1119:                Expr();
1120:
1121:                int opLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos;
1122:
1123:                if (opLen == 2) {
1124:                    error(
1125:                            XPATHErrorResources.ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL,
1126:                            null); //"boolean(...) argument is no longer optional with 19990709 XPath draft.");
1127:                }
1128:
1129:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, opLen);
1130:            }
1131:
1132:            /**
1133:             *
1134:             *
1135:             * NumberExpr  ::=  Expr
1136:             *
1137:             *
1138:             * @throws javax.xml.transform.TransformerException
1139:             */
1140:            protected void NumberExpr()
1141:                    throws javax.xml.transform.TransformerException {
1142:
1143:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1144:
1145:                appendOp(2, OpCodes.OP_NUMBER);
1146:                Expr();
1147:
1148:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1149:                        .getOp(OpMap.MAPINDEX_LENGTH)
1150:                        - opPos);
1151:            }
1152:
1153:            /**
1154:             * The context of the right hand side expressions is the context of the
1155:             * left hand side expression. The results of the right hand side expressions
1156:             * are node sets. The result of the left hand side UnionExpr is the union
1157:             * of the results of the right hand side expressions.
1158:             *
1159:             *
1160:             * UnionExpr    ::=    PathExpr
1161:             * | UnionExpr '|' PathExpr
1162:             *
1163:             *
1164:             * @throws javax.xml.transform.TransformerException
1165:             */
1166:            protected void UnionExpr()
1167:                    throws javax.xml.transform.TransformerException {
1168:
1169:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1170:                boolean continueOrLoop = true;
1171:                boolean foundUnion = false;
1172:
1173:                do {
1174:                    PathExpr();
1175:
1176:                    if (tokenIs('|')) {
1177:                        if (false == foundUnion) {
1178:                            foundUnion = true;
1179:
1180:                            insertOp(opPos, 2, OpCodes.OP_UNION);
1181:                        }
1182:
1183:                        nextToken();
1184:                    } else {
1185:                        break;
1186:                    }
1187:
1188:                    // this.m_testForDocOrder = true;
1189:                } while (continueOrLoop);
1190:
1191:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1192:                        .getOp(OpMap.MAPINDEX_LENGTH)
1193:                        - opPos);
1194:            }
1195:
1196:            /**
1197:             * PathExpr  ::=  LocationPath
1198:             * | FilterExpr
1199:             * | FilterExpr '/' RelativeLocationPath
1200:             * | FilterExpr '//' RelativeLocationPath
1201:             *
1202:             * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
1203:             * the error condition is severe enough to halt processing.
1204:             *
1205:             * @throws javax.xml.transform.TransformerException
1206:             */
1207:            protected void PathExpr()
1208:                    throws javax.xml.transform.TransformerException {
1209:
1210:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1211:
1212:                int filterExprMatch = FilterExpr();
1213:
1214:                if (filterExprMatch != FILTER_MATCH_FAILED) {
1215:                    // If FilterExpr had Predicates, a OP_LOCATIONPATH opcode would already
1216:                    // have been inserted.
1217:                    boolean locationPathStarted = (filterExprMatch == FILTER_MATCH_PREDICATES);
1218:
1219:                    if (tokenIs('/')) {
1220:                        nextToken();
1221:
1222:                        if (!locationPathStarted) {
1223:                            // int locationPathOpPos = opPos;
1224:                            insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
1225:
1226:                            locationPathStarted = true;
1227:                        }
1228:
1229:                        if (!RelativeLocationPath()) {
1230:                            // "Relative location path expected following '/' or '//'"
1231:                            error(XPATHErrorResources.ER_EXPECTED_REL_LOC_PATH,
1232:                                    null);
1233:                        }
1234:
1235:                    }
1236:
1237:                    // Terminate for safety.
1238:                    if (locationPathStarted) {
1239:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1240:                                OpCodes.ENDOP);
1241:                        m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1242:                                .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1243:                        m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1244:                                .getOp(OpMap.MAPINDEX_LENGTH)
1245:                                - opPos);
1246:                    }
1247:                } else {
1248:                    LocationPath();
1249:                }
1250:            }
1251:
1252:            /**
1253:             *
1254:             *
1255:             * FilterExpr  ::=  PrimaryExpr
1256:             * | FilterExpr Predicate
1257:             *
1258:             * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
1259:             * the error condition is severe enough to halt processing.
1260:             *
1261:             * @return  FILTER_MATCH_PREDICATES, if this method successfully matched a
1262:             *          FilterExpr with one or more Predicates;
1263:             *          FILTER_MATCH_PRIMARY, if this method successfully matched a
1264:             *          FilterExpr that was just a PrimaryExpr; or
1265:             *          FILTER_MATCH_FAILED, if this method did not match a FilterExpr
1266:             *
1267:             * @throws javax.xml.transform.TransformerException
1268:             */
1269:            protected int FilterExpr()
1270:                    throws javax.xml.transform.TransformerException {
1271:
1272:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1273:
1274:                int filterMatch;
1275:
1276:                if (PrimaryExpr()) {
1277:                    if (tokenIs('[')) {
1278:
1279:                        // int locationPathOpPos = opPos;
1280:                        insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
1281:
1282:                        while (tokenIs('[')) {
1283:                            Predicate();
1284:                        }
1285:
1286:                        filterMatch = FILTER_MATCH_PREDICATES;
1287:                    } else {
1288:                        filterMatch = FILTER_MATCH_PRIMARY;
1289:                    }
1290:                } else {
1291:                    filterMatch = FILTER_MATCH_FAILED;
1292:                }
1293:
1294:                return filterMatch;
1295:
1296:                /*
1297:                 * if(tokenIs('['))
1298:                 * {
1299:                 *   Predicate();
1300:                 *   m_ops.m_opMap[opPos + OpMap.MAPINDEX_LENGTH] = m_ops.m_opMap[OpMap.MAPINDEX_LENGTH] - opPos;
1301:                 * }
1302:                 */
1303:            }
1304:
1305:            /**
1306:             *
1307:             * PrimaryExpr  ::=  VariableReference
1308:             * | '(' Expr ')'
1309:             * | Literal
1310:             * | Number
1311:             * | FunctionCall
1312:             *
1313:             * @return true if this method successfully matched a PrimaryExpr
1314:             *
1315:             * @throws javax.xml.transform.TransformerException
1316:             *
1317:             */
1318:            protected boolean PrimaryExpr()
1319:                    throws javax.xml.transform.TransformerException {
1320:
1321:                boolean matchFound;
1322:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1323:
1324:                if ((m_tokenChar == '\'') || (m_tokenChar == '"')) {
1325:                    appendOp(2, OpCodes.OP_LITERAL);
1326:                    Literal();
1327:
1328:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1329:                            .getOp(OpMap.MAPINDEX_LENGTH)
1330:                            - opPos);
1331:
1332:                    matchFound = true;
1333:                } else if (m_tokenChar == '$') {
1334:                    nextToken(); // consume '$'
1335:                    appendOp(2, OpCodes.OP_VARIABLE);
1336:                    QName();
1337:
1338:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1339:                            .getOp(OpMap.MAPINDEX_LENGTH)
1340:                            - opPos);
1341:
1342:                    matchFound = true;
1343:                } else if (m_tokenChar == '(') {
1344:                    nextToken();
1345:                    appendOp(2, OpCodes.OP_GROUP);
1346:                    Expr();
1347:                    consumeExpected(')');
1348:
1349:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1350:                            .getOp(OpMap.MAPINDEX_LENGTH)
1351:                            - opPos);
1352:
1353:                    matchFound = true;
1354:                } else if ((null != m_token)
1355:                        && ((('.' == m_tokenChar) && (m_token.length() > 1) && Character
1356:                                .isDigit(m_token.charAt(1))) || Character
1357:                                .isDigit(m_tokenChar))) {
1358:                    appendOp(2, OpCodes.OP_NUMBERLIT);
1359:                    Number();
1360:
1361:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1362:                            .getOp(OpMap.MAPINDEX_LENGTH)
1363:                            - opPos);
1364:
1365:                    matchFound = true;
1366:                } else if (lookahead('(', 1)
1367:                        || (lookahead(':', 1) && lookahead('(', 3))) {
1368:                    matchFound = FunctionCall();
1369:                } else {
1370:                    matchFound = false;
1371:                }
1372:
1373:                return matchFound;
1374:            }
1375:
1376:            /**
1377:             *
1378:             * Argument    ::=    Expr
1379:             *
1380:             *
1381:             * @throws javax.xml.transform.TransformerException
1382:             */
1383:            protected void Argument()
1384:                    throws javax.xml.transform.TransformerException {
1385:
1386:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1387:
1388:                appendOp(2, OpCodes.OP_ARGUMENT);
1389:                Expr();
1390:
1391:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1392:                        .getOp(OpMap.MAPINDEX_LENGTH)
1393:                        - opPos);
1394:            }
1395:
1396:            /**
1397:             *
1398:             * FunctionCall    ::=    FunctionName '(' ( Argument ( ',' Argument)*)? ')'
1399:             *
1400:             * @return true if, and only if, a FunctionCall was matched
1401:             *
1402:             * @throws javax.xml.transform.TransformerException
1403:             */
1404:            protected boolean FunctionCall()
1405:                    throws javax.xml.transform.TransformerException {
1406:
1407:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1408:
1409:                if (lookahead(':', 1)) {
1410:                    appendOp(4, OpCodes.OP_EXTFUNCTION);
1411:
1412:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
1413:                            m_queueMark - 1);
1414:
1415:                    nextToken();
1416:                    consumeExpected(':');
1417:
1418:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 2,
1419:                            m_queueMark - 1);
1420:
1421:                    nextToken();
1422:                } else {
1423:                    int funcTok = getFunctionToken(m_token);
1424:
1425:                    if (-1 == funcTok) {
1426:                        error(XPATHErrorResources.ER_COULDNOT_FIND_FUNCTION,
1427:                                new Object[] { m_token }); //"Could not find function: "+m_token+"()");
1428:                    }
1429:
1430:                    switch (funcTok) {
1431:                    case OpCodes.NODETYPE_PI:
1432:                    case OpCodes.NODETYPE_COMMENT:
1433:                    case OpCodes.NODETYPE_TEXT:
1434:                    case OpCodes.NODETYPE_NODE:
1435:                        // Node type tests look like function calls, but they're not
1436:                        return false;
1437:                    default:
1438:                        appendOp(3, OpCodes.OP_FUNCTION);
1439:
1440:                        m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, funcTok);
1441:                    }
1442:
1443:                    nextToken();
1444:                }
1445:
1446:                consumeExpected('(');
1447:
1448:                while (!tokenIs(')') && m_token != null) {
1449:                    if (tokenIs(',')) {
1450:                        error(
1451:                                XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG,
1452:                                null); //"Found ',' but no preceding argument!");
1453:                    }
1454:
1455:                    Argument();
1456:
1457:                    if (!tokenIs(')')) {
1458:                        consumeExpected(',');
1459:
1460:                        if (tokenIs(')')) {
1461:                            error(
1462:                                    XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG,
1463:                                    null); //"Found ',' but no following argument!");
1464:                        }
1465:                    }
1466:                }
1467:
1468:                consumeExpected(')');
1469:
1470:                // Terminate for safety.
1471:                m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
1472:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1473:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1474:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1475:                        .getOp(OpMap.MAPINDEX_LENGTH)
1476:                        - opPos);
1477:
1478:                return true;
1479:            }
1480:
1481:            // ============= GRAMMAR FUNCTIONS =================
1482:
1483:            /**
1484:             *
1485:             * LocationPath ::= RelativeLocationPath
1486:             * | AbsoluteLocationPath
1487:             *
1488:             *
1489:             * @throws javax.xml.transform.TransformerException
1490:             */
1491:            protected void LocationPath()
1492:                    throws javax.xml.transform.TransformerException {
1493:
1494:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1495:
1496:                // int locationPathOpPos = opPos;
1497:                appendOp(2, OpCodes.OP_LOCATIONPATH);
1498:
1499:                boolean seenSlash = tokenIs('/');
1500:
1501:                if (seenSlash) {
1502:                    appendOp(4, OpCodes.FROM_ROOT);
1503:
1504:                    // Tell how long the step is without the predicate
1505:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
1506:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1,
1507:                            OpCodes.NODETYPE_ROOT);
1508:
1509:                    nextToken();
1510:                } else if (m_token == null) {
1511:                    error(XPATHErrorResources.ER_EXPECTED_LOC_PATH_AT_END_EXPR,
1512:                            null);
1513:                }
1514:
1515:                if (m_token != null) {
1516:                    if (!RelativeLocationPath() && !seenSlash) {
1517:                        // Neither a '/' nor a RelativeLocationPath - i.e., matched nothing
1518:                        // "Location path expected, but found "+m_token+" was encountered."
1519:                        error(XPATHErrorResources.ER_EXPECTED_LOC_PATH,
1520:                                new Object[] { m_token });
1521:                    }
1522:                }
1523:
1524:                // Terminate for safety.
1525:                m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
1526:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1527:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1528:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1529:                        .getOp(OpMap.MAPINDEX_LENGTH)
1530:                        - opPos);
1531:            }
1532:
1533:            /**
1534:             *
1535:             * RelativeLocationPath ::= Step
1536:             * | RelativeLocationPath '/' Step
1537:             * | AbbreviatedRelativeLocationPath
1538:             *
1539:             * @returns true if, and only if, a RelativeLocationPath was matched
1540:             *
1541:             * @throws javax.xml.transform.TransformerException
1542:             */
1543:            protected boolean RelativeLocationPath()
1544:                    throws javax.xml.transform.TransformerException {
1545:                if (!Step()) {
1546:                    return false;
1547:                }
1548:
1549:                while (tokenIs('/')) {
1550:                    nextToken();
1551:
1552:                    if (!Step()) {
1553:                        // RelativeLocationPath can't end with a trailing '/'
1554:                        // "Location step expected following '/' or '//'"
1555:                        error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
1556:                    }
1557:                }
1558:
1559:                return true;
1560:            }
1561:
1562:            /**
1563:             *
1564:             * Step    ::=    Basis Predicate
1565:             * | AbbreviatedStep
1566:             *
1567:             * @returns false if step was empty (or only a '/'); true, otherwise
1568:             *
1569:             * @throws javax.xml.transform.TransformerException
1570:             */
1571:            protected boolean Step()
1572:                    throws javax.xml.transform.TransformerException {
1573:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1574:
1575:                boolean doubleSlash = tokenIs('/');
1576:
1577:                // At most a single '/' before each Step is consumed by caller; if the
1578:                // first thing is a '/', that means we had '//' and the Step must not
1579:                // be empty.
1580:                if (doubleSlash) {
1581:                    nextToken();
1582:
1583:                    appendOp(2, OpCodes.FROM_DESCENDANTS_OR_SELF);
1584:
1585:                    // Have to fix up for patterns such as '//@foo' or '//attribute::foo',
1586:                    // which translate to 'descendant-or-self::node()/attribute::foo'.
1587:                    // notice I leave the '/' on the queue, so the next will be processed
1588:                    // by a regular step pattern.
1589:
1590:                    // Make room for telling how long the step is without the predicate
1591:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1592:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1593:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1594:                            OpCodes.NODETYPE_NODE);
1595:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1596:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1597:
1598:                    // Tell how long the step is without the predicate
1599:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_ops
1600:                            .getOp(OpMap.MAPINDEX_LENGTH)
1601:                            - opPos);
1602:
1603:                    // Tell how long the step is with the predicate
1604:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1605:                            .getOp(OpMap.MAPINDEX_LENGTH)
1606:                            - opPos);
1607:
1608:                    opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1609:                }
1610:
1611:                if (tokenIs(".")) {
1612:                    nextToken();
1613:
1614:                    if (tokenIs('[')) {
1615:                        error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX,
1616:                                null); //"'..[predicate]' or '.[predicate]' is illegal syntax.  Use 'self::node()[predicate]' instead.");
1617:                    }
1618:
1619:                    appendOp(4, OpCodes.FROM_SELF);
1620:
1621:                    // Tell how long the step is without the predicate
1622:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
1623:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1,
1624:                            OpCodes.NODETYPE_NODE);
1625:                } else if (tokenIs("..")) {
1626:                    nextToken();
1627:                    appendOp(4, OpCodes.FROM_PARENT);
1628:
1629:                    // Tell how long the step is without the predicate
1630:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
1631:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1,
1632:                            OpCodes.NODETYPE_NODE);
1633:                }
1634:
1635:                // There is probably a better way to test for this 
1636:                // transition... but it gets real hairy if you try 
1637:                // to do it in basis().
1638:                else if (tokenIs('*')
1639:                        || tokenIs('@')
1640:                        || tokenIs('_')
1641:                        || (m_token != null && Character.isLetter(m_token
1642:                                .charAt(0)))) {
1643:                    Basis();
1644:
1645:                    while (tokenIs('[')) {
1646:                        Predicate();
1647:                    }
1648:
1649:                    // Tell how long the entire step is.
1650:                    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1651:                            .getOp(OpMap.MAPINDEX_LENGTH)
1652:                            - opPos);
1653:                } else {
1654:                    // No Step matched - that's an error if previous thing was a '//'
1655:                    if (doubleSlash) {
1656:                        // "Location step expected following '/' or '//'"
1657:                        error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
1658:                    }
1659:
1660:                    return false;
1661:                }
1662:
1663:                return true;
1664:            }
1665:
1666:            /**
1667:             *
1668:             * Basis    ::=    AxisName '::' NodeTest
1669:             * | AbbreviatedBasis
1670:             *
1671:             * @throws javax.xml.transform.TransformerException
1672:             */
1673:            protected void Basis()
1674:                    throws javax.xml.transform.TransformerException {
1675:
1676:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1677:                int axesType;
1678:
1679:                // The next blocks guarantee that a FROM_XXX will be added.
1680:                if (lookahead("::", 1)) {
1681:                    axesType = AxisName();
1682:
1683:                    nextToken();
1684:                    nextToken();
1685:                } else if (tokenIs('@')) {
1686:                    axesType = OpCodes.FROM_ATTRIBUTES;
1687:
1688:                    appendOp(2, axesType);
1689:                    nextToken();
1690:                } else {
1691:                    axesType = OpCodes.FROM_CHILDREN;
1692:
1693:                    appendOp(2, axesType);
1694:                }
1695:
1696:                // Make room for telling how long the step is without the predicate
1697:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1698:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1699:
1700:                NodeTest(axesType);
1701:
1702:                // Tell how long the step is without the predicate
1703:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_ops
1704:                        .getOp(OpMap.MAPINDEX_LENGTH)
1705:                        - opPos);
1706:            }
1707:
1708:            /**
1709:             *
1710:             * Basis    ::=    AxisName '::' NodeTest
1711:             * | AbbreviatedBasis
1712:             *
1713:             * @return FROM_XXX axes type, found in {@link org.apache.xpath.compiler.Keywords}.
1714:             *
1715:             * @throws javax.xml.transform.TransformerException
1716:             */
1717:            protected int AxisName()
1718:                    throws javax.xml.transform.TransformerException {
1719:
1720:                Object val = Keywords.getAxisName(m_token);
1721:
1722:                if (null == val) {
1723:                    error(XPATHErrorResources.ER_ILLEGAL_AXIS_NAME,
1724:                            new Object[] { m_token }); //"illegal axis name: "+m_token);
1725:                }
1726:
1727:                int axesType = ((Integer) val).intValue();
1728:
1729:                appendOp(2, axesType);
1730:
1731:                return axesType;
1732:            }
1733:
1734:            /**
1735:             *
1736:             * NodeTest    ::=    WildcardName
1737:             * | NodeType '(' ')'
1738:             * | 'processing-instruction' '(' Literal ')'
1739:             *
1740:             * @param axesType FROM_XXX axes type, found in {@link org.apache.xpath.compiler.Keywords}.
1741:             *
1742:             * @throws javax.xml.transform.TransformerException
1743:             */
1744:            protected void NodeTest(int axesType)
1745:                    throws javax.xml.transform.TransformerException {
1746:
1747:                if (lookahead('(', 1)) {
1748:                    Object nodeTestOp = Keywords.getNodeType(m_token);
1749:
1750:                    if (null == nodeTestOp) {
1751:                        error(XPATHErrorResources.ER_UNKNOWN_NODETYPE,
1752:                                new Object[] { m_token }); //"Unknown nodetype: "+m_token);
1753:                    } else {
1754:                        nextToken();
1755:
1756:                        int nt = ((Integer) nodeTestOp).intValue();
1757:
1758:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), nt);
1759:                        m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1760:                                .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1761:
1762:                        consumeExpected('(');
1763:
1764:                        if (OpCodes.NODETYPE_PI == nt) {
1765:                            if (!tokenIs(')')) {
1766:                                Literal();
1767:                            }
1768:                        }
1769:
1770:                        consumeExpected(')');
1771:                    }
1772:                } else {
1773:
1774:                    // Assume name of attribute or element.
1775:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1776:                            OpCodes.NODENAME);
1777:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1778:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1779:
1780:                    if (lookahead(':', 1)) {
1781:                        if (tokenIs('*')) {
1782:                            m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1783:                                    OpCodes.ELEMWILDCARD);
1784:                        } else {
1785:                            m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1786:                                    m_queueMark - 1);
1787:
1788:                            // Minimalist check for an NCName - just check first character
1789:                            // to distinguish from other possible tokens
1790:                            if (!Character.isLetter(m_tokenChar)
1791:                                    && !tokenIs('_')) {
1792:                                // "Node test that matches either NCName:* or QName was expected."
1793:                                error(
1794:                                        XPATHErrorResources.ER_EXPECTED_NODE_TEST,
1795:                                        null);
1796:                            }
1797:                        }
1798:
1799:                        nextToken();
1800:                        consumeExpected(':');
1801:                    } else {
1802:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1803:                                OpCodes.EMPTY);
1804:                    }
1805:
1806:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1807:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1808:
1809:                    if (tokenIs('*')) {
1810:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1811:                                OpCodes.ELEMWILDCARD);
1812:                    } else {
1813:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1814:                                m_queueMark - 1);
1815:
1816:                        // Minimalist check for an NCName - just check first character
1817:                        // to distinguish from other possible tokens
1818:                        if (!Character.isLetter(m_tokenChar) && !tokenIs('_')) {
1819:                            // "Node test that matches either NCName:* or QName was expected."
1820:                            error(XPATHErrorResources.ER_EXPECTED_NODE_TEST,
1821:                                    null);
1822:                        }
1823:                    }
1824:
1825:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1826:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1827:
1828:                    nextToken();
1829:                }
1830:            }
1831:
1832:            /**
1833:             *
1834:             * Predicate ::= '[' PredicateExpr ']'
1835:             *
1836:             *
1837:             * @throws javax.xml.transform.TransformerException
1838:             */
1839:            protected void Predicate()
1840:                    throws javax.xml.transform.TransformerException {
1841:
1842:                if (tokenIs('[')) {
1843:                    nextToken();
1844:                    PredicateExpr();
1845:                    consumeExpected(']');
1846:                }
1847:            }
1848:
1849:            /**
1850:             *
1851:             * PredicateExpr ::= Expr
1852:             *
1853:             *
1854:             * @throws javax.xml.transform.TransformerException
1855:             */
1856:            protected void PredicateExpr()
1857:                    throws javax.xml.transform.TransformerException {
1858:
1859:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
1860:
1861:                appendOp(2, OpCodes.OP_PREDICATE);
1862:                Expr();
1863:
1864:                // Terminate for safety.
1865:                m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
1866:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1867:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1868:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
1869:                        .getOp(OpMap.MAPINDEX_LENGTH)
1870:                        - opPos);
1871:            }
1872:
1873:            /**
1874:             * QName ::=  (Prefix ':')? LocalPart
1875:             * Prefix ::=  NCName
1876:             * LocalPart ::=  NCName
1877:             *
1878:             * @throws javax.xml.transform.TransformerException
1879:             */
1880:            protected void QName()
1881:                    throws javax.xml.transform.TransformerException {
1882:                // Namespace
1883:                if (lookahead(':', 1)) {
1884:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1885:                            m_queueMark - 1);
1886:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1887:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1888:
1889:                    nextToken();
1890:                    consumeExpected(':');
1891:                } else {
1892:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1893:                            OpCodes.EMPTY);
1894:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1895:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1896:                }
1897:
1898:                // Local name
1899:                m_ops
1900:                        .setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1901:                                m_queueMark - 1);
1902:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1903:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1904:
1905:                nextToken();
1906:            }
1907:
1908:            /**
1909:             * NCName ::=  (Letter | '_') (NCNameChar)
1910:             * NCNameChar ::=  Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
1911:             */
1912:            protected void NCName() {
1913:
1914:                m_ops
1915:                        .setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1916:                                m_queueMark - 1);
1917:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1918:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1919:
1920:                nextToken();
1921:            }
1922:
1923:            /**
1924:             * The value of the Literal is the sequence of characters inside
1925:             * the " or ' characters>.
1926:             *
1927:             * Literal  ::=  '"' [^"]* '"'
1928:             * | "'" [^']* "'"
1929:             *
1930:             *
1931:             * @throws javax.xml.transform.TransformerException
1932:             */
1933:            protected void Literal()
1934:                    throws javax.xml.transform.TransformerException {
1935:
1936:                int last = m_token.length() - 1;
1937:                char c0 = m_tokenChar;
1938:                char cX = m_token.charAt(last);
1939:
1940:                if (((c0 == '\"') && (cX == '\"'))
1941:                        || ((c0 == '\'') && (cX == '\''))) {
1942:
1943:                    // Mutate the token to remove the quotes and have the XString object
1944:                    // already made.
1945:                    int tokenQueuePos = m_queueMark - 1;
1946:
1947:                    m_ops.m_tokenQueue.setElementAt(null, tokenQueuePos);
1948:
1949:                    Object obj = new XString(m_token.substring(1, last));
1950:
1951:                    m_ops.m_tokenQueue.setElementAt(obj, tokenQueuePos);
1952:
1953:                    // lit = m_token.substring(1, last);
1954:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
1955:                            tokenQueuePos);
1956:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
1957:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
1958:
1959:                    nextToken();
1960:                } else {
1961:                    error(
1962:                            XPATHErrorResources.ER_PATTERN_LITERAL_NEEDS_BE_QUOTED,
1963:                            new Object[] { m_token }); //"Pattern literal ("+m_token+") needs to be quoted!");
1964:                }
1965:            }
1966:
1967:            /**
1968:             *
1969:             * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
1970:             *
1971:             *
1972:             * @throws javax.xml.transform.TransformerException
1973:             */
1974:            protected void Number()
1975:                    throws javax.xml.transform.TransformerException {
1976:
1977:                if (null != m_token) {
1978:
1979:                    // Mutate the token to remove the quotes and have the XNumber object
1980:                    // already made.
1981:                    double num;
1982:
1983:                    try {
1984:                        // XPath 1.0 does not support number in exp notation
1985:                        if ((m_token.indexOf('e') > -1)
1986:                                || (m_token.indexOf('E') > -1))
1987:                            throw new NumberFormatException();
1988:                        num = Double.valueOf(m_token).doubleValue();
1989:                    } catch (NumberFormatException nfe) {
1990:                        num = 0.0; // to shut up compiler.
1991:
1992:                        error(
1993:                                XPATHErrorResources.ER_COULDNOT_BE_FORMATTED_TO_NUMBER,
1994:                                new Object[] { m_token }); //m_token+" could not be formatted to a number!");
1995:                    }
1996:
1997:                    m_ops.m_tokenQueue.setElementAt(new XNumber(num),
1998:                            m_queueMark - 1);
1999:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH),
2000:                            m_queueMark - 1);
2001:                    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
2002:                            .getOp(OpMap.MAPINDEX_LENGTH) + 1);
2003:
2004:                    nextToken();
2005:                }
2006:            }
2007:
2008:            // ============= PATTERN FUNCTIONS =================
2009:
2010:            /**
2011:             *
2012:             * Pattern  ::=  LocationPathPattern
2013:             * | Pattern '|' LocationPathPattern
2014:             *
2015:             *
2016:             * @throws javax.xml.transform.TransformerException
2017:             */
2018:            protected void Pattern()
2019:                    throws javax.xml.transform.TransformerException {
2020:
2021:                while (true) {
2022:                    LocationPathPattern();
2023:
2024:                    if (tokenIs('|')) {
2025:                        nextToken();
2026:                    } else {
2027:                        break;
2028:                    }
2029:                }
2030:            }
2031:
2032:            /**
2033:             *
2034:             *
2035:             * LocationPathPattern  ::=  '/' RelativePathPattern?
2036:             * | IdKeyPattern (('/' | '//') RelativePathPattern)?
2037:             * | '//'? RelativePathPattern
2038:             *
2039:             *
2040:             * @throws javax.xml.transform.TransformerException
2041:             */
2042:            protected void LocationPathPattern()
2043:                    throws javax.xml.transform.TransformerException {
2044:
2045:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
2046:
2047:                final int RELATIVE_PATH_NOT_PERMITTED = 0;
2048:                final int RELATIVE_PATH_PERMITTED = 1;
2049:                final int RELATIVE_PATH_REQUIRED = 2;
2050:
2051:                int relativePathStatus = RELATIVE_PATH_NOT_PERMITTED;
2052:
2053:                appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN);
2054:
2055:                if (lookahead('(', 1)
2056:                        && (tokenIs(Keywords.FUNC_ID_STRING) || tokenIs(Keywords.FUNC_KEY_STRING))) {
2057:                    IdKeyPattern();
2058:
2059:                    if (tokenIs('/')) {
2060:                        nextToken();
2061:
2062:                        if (tokenIs('/')) {
2063:                            appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
2064:
2065:                            nextToken();
2066:                        } else {
2067:                            appendOp(4, OpCodes.MATCH_IMMEDIATE_ANCESTOR);
2068:                        }
2069:
2070:                        // Tell how long the step is without the predicate
2071:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
2072:                        m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1,
2073:                                OpCodes.NODETYPE_FUNCTEST);
2074:
2075:                        relativePathStatus = RELATIVE_PATH_REQUIRED;
2076:                    }
2077:                } else if (tokenIs('/')) {
2078:                    if (lookahead('/', 1)) {
2079:                        appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
2080:
2081:                        // Added this to fix bug reported by Myriam for match="//x/a"
2082:                        // patterns.  If you don't do this, the 'x' step will think it's part
2083:                        // of a '//' pattern, and so will cause 'a' to be matched when it has
2084:                        // any ancestor that is 'x'.
2085:                        nextToken();
2086:
2087:                        relativePathStatus = RELATIVE_PATH_REQUIRED;
2088:                    } else {
2089:                        appendOp(4, OpCodes.FROM_ROOT);
2090:
2091:                        relativePathStatus = RELATIVE_PATH_PERMITTED;
2092:                    }
2093:
2094:                    // Tell how long the step is without the predicate
2095:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
2096:                    m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1,
2097:                            OpCodes.NODETYPE_ROOT);
2098:
2099:                    nextToken();
2100:                } else {
2101:                    relativePathStatus = RELATIVE_PATH_REQUIRED;
2102:                }
2103:
2104:                if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED) {
2105:                    if (!tokenIs('|') && (null != m_token)) {
2106:                        RelativePathPattern();
2107:                    } else if (relativePathStatus == RELATIVE_PATH_REQUIRED) {
2108:                        // "A relative path pattern was expected."
2109:                        error(XPATHErrorResources.ER_EXPECTED_REL_PATH_PATTERN,
2110:                                null);
2111:                    }
2112:                }
2113:
2114:                // Terminate for safety.
2115:                m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
2116:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
2117:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
2118:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
2119:                        .getOp(OpMap.MAPINDEX_LENGTH)
2120:                        - opPos);
2121:            }
2122:
2123:            /**
2124:             *
2125:             * IdKeyPattern  ::=  'id' '(' Literal ')'
2126:             * | 'key' '(' Literal ',' Literal ')'
2127:             * (Also handle doc())
2128:             *
2129:             *
2130:             * @throws javax.xml.transform.TransformerException
2131:             */
2132:            protected void IdKeyPattern()
2133:                    throws javax.xml.transform.TransformerException {
2134:                FunctionCall();
2135:            }
2136:
2137:            /**
2138:             *
2139:             * RelativePathPattern  ::=  StepPattern
2140:             * | RelativePathPattern '/' StepPattern
2141:             * | RelativePathPattern '//' StepPattern
2142:             *
2143:             * @throws javax.xml.transform.TransformerException
2144:             */
2145:            protected void RelativePathPattern()
2146:                    throws javax.xml.transform.TransformerException {
2147:
2148:                // Caller will have consumed any '/' or '//' preceding the
2149:                // RelativePathPattern, so let StepPattern know it can't begin with a '/'
2150:                boolean trailingSlashConsumed = StepPattern(false);
2151:
2152:                while (tokenIs('/')) {
2153:                    nextToken();
2154:
2155:                    // StepPattern() may consume first slash of pair in "a//b" while
2156:                    // processing StepPattern "a".  On next iteration, let StepPattern know
2157:                    // that happened, so it doesn't match ill-formed patterns like "a///b".
2158:                    trailingSlashConsumed = StepPattern(!trailingSlashConsumed);
2159:                }
2160:            }
2161:
2162:            /**
2163:             *
2164:             * StepPattern  ::=  AbbreviatedNodeTestStep
2165:             *
2166:             * @param isLeadingSlashPermitted a boolean indicating whether a slash can
2167:             *        appear at the start of this step
2168:             *
2169:             * @return boolean indicating whether a slash following the step was consumed
2170:             *
2171:             * @throws javax.xml.transform.TransformerException
2172:             */
2173:            protected boolean StepPattern(boolean isLeadingSlashPermitted)
2174:                    throws javax.xml.transform.TransformerException {
2175:                return AbbreviatedNodeTestStep(isLeadingSlashPermitted);
2176:            }
2177:
2178:            /**
2179:             *
2180:             * AbbreviatedNodeTestStep    ::=    '@'? NodeTest Predicate
2181:             *
2182:             * @param isLeadingSlashPermitted a boolean indicating whether a slash can
2183:             *        appear at the start of this step
2184:             *
2185:             * @return boolean indicating whether a slash following the step was consumed
2186:             *
2187:             * @throws javax.xml.transform.TransformerException
2188:             */
2189:            protected boolean AbbreviatedNodeTestStep(
2190:                    boolean isLeadingSlashPermitted)
2191:                    throws javax.xml.transform.TransformerException {
2192:
2193:                int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
2194:                int axesType;
2195:
2196:                // The next blocks guarantee that a MATCH_XXX will be added.
2197:                int matchTypePos = -1;
2198:
2199:                if (tokenIs('@')) {
2200:                    axesType = OpCodes.MATCH_ATTRIBUTE;
2201:
2202:                    appendOp(2, axesType);
2203:                    nextToken();
2204:                } else if (this .lookahead("::", 1)) {
2205:                    if (tokenIs("attribute")) {
2206:                        axesType = OpCodes.MATCH_ATTRIBUTE;
2207:
2208:                        appendOp(2, axesType);
2209:                    } else if (tokenIs("child")) {
2210:                        matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
2211:                        axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
2212:
2213:                        appendOp(2, axesType);
2214:                    } else {
2215:                        axesType = -1;
2216:
2217:                        this .error(XPATHErrorResources.ER_AXES_NOT_ALLOWED,
2218:                                new Object[] { this .m_token });
2219:                    }
2220:
2221:                    nextToken();
2222:                    nextToken();
2223:                } else if (tokenIs('/')) {
2224:                    if (!isLeadingSlashPermitted) {
2225:                        // "A step was expected in the pattern, but '/' was encountered."
2226:                        error(XPATHErrorResources.ER_EXPECTED_STEP_PATTERN,
2227:                                null);
2228:                    }
2229:                    axesType = OpCodes.MATCH_ANY_ANCESTOR;
2230:
2231:                    appendOp(2, axesType);
2232:                    nextToken();
2233:                } else {
2234:                    matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
2235:                    axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
2236:
2237:                    appendOp(2, axesType);
2238:                }
2239:
2240:                // Make room for telling how long the step is without the predicate
2241:                m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops
2242:                        .getOp(OpMap.MAPINDEX_LENGTH) + 1);
2243:
2244:                NodeTest(axesType);
2245:
2246:                // Tell how long the step is without the predicate
2247:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_ops
2248:                        .getOp(OpMap.MAPINDEX_LENGTH)
2249:                        - opPos);
2250:
2251:                while (tokenIs('[')) {
2252:                    Predicate();
2253:                }
2254:
2255:                boolean trailingSlashConsumed;
2256:
2257:                // For "a//b", where "a" is current step, we need to mark operation of
2258:                // current step as "MATCH_ANY_ANCESTOR".  Then we'll consume the first
2259:                // slash and subsequent step will be treated as a MATCH_IMMEDIATE_ANCESTOR
2260:                // (unless it too is followed by '//'.)
2261:                //
2262:                // %REVIEW%  Following is what happens today, but I'm not sure that's
2263:                // %REVIEW%  correct behaviour.  Perhaps no valid case could be constructed
2264:                // %REVIEW%  where it would matter?
2265:                //
2266:                // If current step is on the attribute axis (e.g., "@x//b"), we won't
2267:                // change the current step, and let following step be marked as
2268:                // MATCH_ANY_ANCESTOR on next call instead.
2269:                if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1)) {
2270:                    m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR);
2271:
2272:                    nextToken();
2273:
2274:                    trailingSlashConsumed = true;
2275:                } else {
2276:                    trailingSlashConsumed = false;
2277:                }
2278:
2279:                // Tell how long the entire step is.
2280:                m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops
2281:                        .getOp(OpMap.MAPINDEX_LENGTH)
2282:                        - opPos);
2283:
2284:                return trailingSlashConsumed;
2285:            }
2286:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.