📄 xpathparser.java
字号:
/** * * Predicate ::= '[' PredicateExpr ']' * * * @throws javax.xml.transform.TransformerException */ protected void Predicate() throws javax.xml.transform.TransformerException { if (tokenIs('[')) { nextToken(); PredicateExpr(); consumeExpected(']'); } } /** * * PredicateExpr ::= Expr * * * @throws javax.xml.transform.TransformerException */ protected void PredicateExpr() throws javax.xml.transform.TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); appendOp(2, OpCodes.OP_PREDICATE); Expr(); // Terminate for safety. m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); } /** * QName ::= (Prefix ':')? LocalPart * Prefix ::= NCName * LocalPart ::= NCName * * @throws javax.xml.transform.TransformerException */ protected void QName() throws javax.xml.transform.TransformerException { // Namespace if(lookahead(':', 1)) { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); nextToken(); consumeExpected(':'); } else { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.EMPTY); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); } // Local name m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); nextToken(); } /** * NCName ::= (Letter | '_') (NCNameChar) * NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender */ protected void NCName() { m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); nextToken(); } /** * The value of the Literal is the sequence of characters inside * the " or ' characters>. * * Literal ::= '"' [^"]* '"' * | "'" [^']* "'" * * * @throws javax.xml.transform.TransformerException */ protected void Literal() throws javax.xml.transform.TransformerException { int last = m_token.length() - 1; char c0 = m_tokenChar; char cX = m_token.charAt(last); if (((c0 == '\"') && (cX == '\"')) || ((c0 == '\'') && (cX == '\''))) { // Mutate the token to remove the quotes and have the XString object // already made. int tokenQueuePos = m_queueMark - 1; m_ops.m_tokenQueue.setElementAt(null,tokenQueuePos); Object obj = new XString(m_token.substring(1, last)); m_ops.m_tokenQueue.setElementAt(obj,tokenQueuePos); // lit = m_token.substring(1, last); m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), tokenQueuePos); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); nextToken(); } else { error(XPATHErrorResources.ER_PATTERN_LITERAL_NEEDS_BE_QUOTED, new Object[]{ m_token }); //"Pattern literal ("+m_token+") needs to be quoted!"); } } /** * * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+ * * * @throws javax.xml.transform.TransformerException */ protected void Number() throws javax.xml.transform.TransformerException { if (null != m_token) { // Mutate the token to remove the quotes and have the XNumber object // already made. double num; try { // XPath 1.0 does not support number in exp notation if ((m_token.indexOf('e') > -1)||(m_token.indexOf('E') > -1)) throw new NumberFormatException(); num = Double.valueOf(m_token).doubleValue(); } catch (NumberFormatException nfe) { num = 0.0; // to shut up compiler. error(XPATHErrorResources.ER_COULDNOT_BE_FORMATTED_TO_NUMBER, new Object[]{ m_token }); //m_token+" could not be formatted to a number!"); } m_ops.m_tokenQueue.setElementAt(new XNumber(num),m_queueMark - 1); m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); nextToken(); } } // ============= PATTERN FUNCTIONS ================= /** * * Pattern ::= LocationPathPattern * | Pattern '|' LocationPathPattern * * * @throws javax.xml.transform.TransformerException */ protected void Pattern() throws javax.xml.transform.TransformerException { while (true) { LocationPathPattern(); if (tokenIs('|')) { nextToken(); } else { break; } } } /** * * * LocationPathPattern ::= '/' RelativePathPattern? * | IdKeyPattern (('/' | '//') RelativePathPattern)? * | '//'? RelativePathPattern * * * @throws javax.xml.transform.TransformerException */ protected void LocationPathPattern() throws javax.xml.transform.TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); final int RELATIVE_PATH_NOT_PERMITTED = 0; final int RELATIVE_PATH_PERMITTED = 1; final int RELATIVE_PATH_REQUIRED = 2; int relativePathStatus = RELATIVE_PATH_NOT_PERMITTED; appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN); if (lookahead('(', 1) && (tokenIs(Keywords.FUNC_ID_STRING) || tokenIs(Keywords.FUNC_KEY_STRING))) { IdKeyPattern(); if (tokenIs('/')) { nextToken(); if (tokenIs('/')) { appendOp(4, OpCodes.MATCH_ANY_ANCESTOR); nextToken(); } else { appendOp(4, OpCodes.MATCH_IMMEDIATE_ANCESTOR); } // Tell how long the step is without the predicate m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4); m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_FUNCTEST); relativePathStatus = RELATIVE_PATH_REQUIRED; } } else if (tokenIs('/')) { if (lookahead('/', 1)) { appendOp(4, OpCodes.MATCH_ANY_ANCESTOR); // Added this to fix bug reported by Myriam for match="//x/a" // patterns. If you don't do this, the 'x' step will think it's part // of a '//' pattern, and so will cause 'a' to be matched when it has // any ancestor that is 'x'. nextToken(); relativePathStatus = RELATIVE_PATH_REQUIRED; } else { appendOp(4, OpCodes.FROM_ROOT); relativePathStatus = RELATIVE_PATH_PERMITTED; } // Tell how long the step is without the predicate m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4); m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_ROOT); nextToken(); } else { relativePathStatus = RELATIVE_PATH_REQUIRED; } if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED) { if (!tokenIs('|') && (null != m_token)) { RelativePathPattern(); } else if (relativePathStatus == RELATIVE_PATH_REQUIRED) { // "A relative path pattern was expected." error(XPATHErrorResources.ER_EXPECTED_REL_PATH_PATTERN, null); } } // Terminate for safety. m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP); m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); } /** * * IdKeyPattern ::= 'id' '(' Literal ')' * | 'key' '(' Literal ',' Literal ')' * (Also handle doc()) * * * @throws javax.xml.transform.TransformerException */ protected void IdKeyPattern() throws javax.xml.transform.TransformerException { FunctionCall(); } /** * * RelativePathPattern ::= StepPattern * | RelativePathPattern '/' StepPattern * | RelativePathPattern '//' StepPattern * * @throws javax.xml.transform.TransformerException */ protected void RelativePathPattern() throws javax.xml.transform.TransformerException { // Caller will have consumed any '/' or '//' preceding the // RelativePathPattern, so let StepPattern know it can't begin with a '/' boolean trailingSlashConsumed = StepPattern(false); while (tokenIs('/')) { nextToken(); // StepPattern() may consume first slash of pair in "a//b" while // processing StepPattern "a". On next iteration, let StepPattern know // that happened, so it doesn't match ill-formed patterns like "a///b". trailingSlashConsumed = StepPattern(!trailingSlashConsumed); } } /** * * StepPattern ::= AbbreviatedNodeTestStep * * @param isLeadingSlashPermitted a boolean indicating whether a slash can * appear at the start of this step * * @return boolean indicating whether a slash following the step was consumed * * @throws javax.xml.transform.TransformerException */ protected boolean StepPattern(boolean isLeadingSlashPermitted) throws javax.xml.transform.TransformerException { return AbbreviatedNodeTestStep(isLeadingSlashPermitted); } /** * * AbbreviatedNodeTestStep ::= '@'? NodeTest Predicate * * @param isLeadingSlashPermitted a boolean indicating whether a slash can * appear at the start of this step * * @return boolean indicating whether a slash following the step was consumed * * @throws javax.xml.transform.TransformerException */ protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted) throws javax.xml.transform.TransformerException { int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); int axesType; // The next blocks guarantee that a MATCH_XXX will be added. int matchTypePos = -1; if (tokenIs('@')) { axesType = OpCodes.MATCH_ATTRIBUTE; appendOp(2, axesType); nextToken(); } else if (this.lookahead("::", 1)) { if (tokenIs("attribute")) { axesType = OpCodes.MATCH_ATTRIBUTE; appendOp(2, axesType); } else if (tokenIs("child")) { matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR; appendOp(2, axesType); } else { axesType = -1; this.error(XPATHErrorResources.ER_AXES_NOT_ALLOWED, new Object[]{ this.m_token }); } nextToken(); nextToken(); } else if (tokenIs('/')) { if (!isLeadingSlashPermitted) { // "A step was expected in the pattern, but '/' was encountered." error(XPATHErrorResources.ER_EXPECTED_STEP_PATTERN, null); } axesType = OpCodes.MATCH_ANY_ANCESTOR; appendOp(2, axesType); nextToken(); } else { matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH); axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR; appendOp(2, axesType); } // Make room for telling how long the step is without the predicate m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1); NodeTest(axesType); // Tell how long the step is without the predicate m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); while (tokenIs('[')) { Predicate(); } boolean trailingSlashConsumed; // For "a//b", where "a" is current step, we need to mark operation of // current step as "MATCH_ANY_ANCESTOR". Then we'll consume the first // slash and subsequent step will be treated as a MATCH_IMMEDIATE_ANCESTOR // (unless it too is followed by '//'.) // // %REVIEW% Following is what happens today, but I'm not sure that's // %REVIEW% correct behaviour. Perhaps no valid case could be constructed // %REVIEW% where it would matter? // // If current step is on the attribute axis (e.g., "@x//b"), we won't // change the current step, and let following step be marked as // MATCH_ANY_ANCESTOR on next call instead. if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1)) { m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR); nextToken(); trailingSlashConsumed = true; } else { trailingSlashConsumed = false; } // Tell how long the entire step is. m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos); return trailingSlashConsumed; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -