| /******************************************************************************* |
| * Copyright (c) 2006 Oracle Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.bpel.validator.xpath; |
| |
| /** |
| * Validator BPEL model dependency |
| */ |
| |
| import java.net.URISyntaxException; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import javax.xml.XMLConstants; |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.bpel.validator.model.ARule; |
| import org.eclipse.bpel.validator.model.IConstants; |
| import org.eclipse.bpel.validator.model.IFunctionMeta; |
| import org.eclipse.bpel.validator.model.IModelQuery; |
| import org.eclipse.bpel.validator.model.IModelQueryLookups; |
| import org.eclipse.bpel.validator.model.INode; |
| import org.eclipse.bpel.validator.model.IProblem; |
| import org.eclipse.bpel.validator.model.Validator; |
| import org.eclipse.bpel.validator.tools.ParserTool; |
| import org.eclipse.bpel.xpath10.AdditiveExpr; |
| import org.eclipse.bpel.xpath10.EqualityExpr; |
| import org.eclipse.bpel.xpath10.Expr; |
| import org.eclipse.bpel.xpath10.FunctionCallExpr; |
| import org.eclipse.bpel.xpath10.LiteralExpr; |
| import org.eclipse.bpel.xpath10.LogicalExpr; |
| import org.eclipse.bpel.xpath10.MultiplicativeExpr; |
| import org.eclipse.bpel.xpath10.NumberExpr; |
| import org.eclipse.bpel.xpath10.RelationalExpr; |
| import org.eclipse.bpel.xpath10.UnaryExpr; |
| import org.eclipse.bpel.xpath10.VariableReferenceExpr; |
| import org.eclipse.bpel.xpath10.parser.XPath10Exception; |
| import org.eclipse.bpel.xpath10.parser.XPath10Factory; |
| |
| /** |
| * This validator validates the XPath expressions used in the BPEL source. |
| * <p> |
| * The validation consists of 2 steps: |
| * <ul> |
| * <li>Compilation and syntax check,and |
| * <li>Static analysis on the expression |
| * </ul> |
| * <p> |
| * The static analysis is performed by visiting the parsed expression tree and |
| * checking certain things against the actual BPEL model. |
| * <p> |
| * The nodes which may contain XPath expressions in BPEL 2.0 and their expected |
| * type is as follows: |
| * |
| * <ul> |
| * <li>branches - unsigned integer |
| * <li>condition - boolean |
| * <li>finalCounterValue - unsigned integer |
| * <li>for - duration |
| * <li>from - any |
| * <li>joinCondition - boolean |
| * <li>repeatEvery - duration |
| * <li>startCounterValue - unsigned integer |
| * <li>to - variable reference expression ($var/foo) |
| * <li>transitionCondition - boolean |
| * <li>until - deadline |
| * |
| * </ul> |
| * |
| * |
| * <p> |
| * |
| * @author Michal Chmielewski (michal.chmielewski@oracle.com) |
| * @date Sep 27, 2006 |
| * |
| */ |
| |
| @SuppressWarnings({ "nls", "boxing" }) |
| public class XPathValidator extends Validator { |
| |
| /** The string format of the XPath expression */ |
| String exprString; |
| |
| String exprStringTrimmed; |
| |
| /** the parsed XPath expression */ |
| Expr xpathExpr; |
| |
| /** For static analysis */ |
| XPathVisitor mVisitor; |
| |
| /** expression name by node name */ |
| protected String fExprByNode; |
| |
| /** parent node */ |
| protected INode fParentNode; |
| |
| /** |
| * Start the validation of this XPathExpression |
| */ |
| |
| @Override |
| public void start() { |
| super.start(); |
| |
| exprString = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_TEXT_TEXT, null, null); |
| |
| exprStringTrimmed = exprString.trim(); |
| |
| fParentNode = mNode.parentNode(); |
| |
| /** fExprByNode is a key to a localization map */ |
| fExprByNode = "text.node." + mNode.nodeName().getLocalPart(); |
| } |
| |
| /** |
| * Do a quick sanity check. |
| */ |
| |
| @ARule( |
| desc = "XPath sanity check", |
| author = "michal.chmielewski@oracle.com", |
| date = "02/16/2007", |
| errors="XPATH_EMPTY_EXPRESSION" |
| ) |
| public void rule_SanityCheck_1() { |
| IProblem problem; |
| |
| if (isEmptyOrWhitespace(exprString)) { |
| problem = createError(); |
| problem.fill("XPATH_EMPTY_EXPRESSION", toString(mNode.nodeName()), |
| fExprByNode); |
| exprString = null; |
| |
| disableRules(); |
| } |
| } |
| |
| /** |
| * Check syntax of this expression ... |
| * |
| */ |
| |
| @ARule( |
| desc = "XPath syntax check", |
| author = "michal.chmielewski@oracle.com", |
| date = "02/16/2007", |
| errors="XPATH_EXPRESSION_SYNTAX" |
| ) |
| public void rule_CheckExpressionSyntax_2() { |
| |
| if (exprString == null) { |
| return; |
| } |
| |
| IProblem problem; |
| |
| try { |
| xpathExpr = XPath10Factory.create(exprString); |
| } catch (XPath10Exception e) { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_SYNTAX", toString(mNode.nodeName()), |
| exprStringTrimmed, fExprByNode, e.getMessage()); |
| repointOffsets(problem, e.getPosition(), e.getPosition() + 3); |
| // TODO: Position in the expression ... ? |
| disableRules(); |
| } |
| } |
| |
| protected void repointOffsets(IProblem problem, Expr expr) { |
| if (expr.getPosition() < 0 || expr.getEndPosition() < 0) { |
| return; |
| } |
| repointOffsets(problem, expr.getPosition(), expr.getEndPosition()); |
| } |
| |
| /** |
| * @param problem |
| * @param offset |
| * @param offsetEnd |
| */ |
| protected void repointOffsets(IProblem problem, int offset, int offsetEnd) { |
| |
| int charStart = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NUMBER_CHAR_START_2, -1); |
| int charEnd = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NUMBER_CHAR_END_2, -1); |
| |
| int lineNo = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NUMBER_LINE_NO_2, -1); |
| int columnNo = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NUMBER_COLUMN_NO_2, -1); |
| |
| if (charStart < 0 || charEnd < 0) { |
| return; |
| } |
| |
| charStart += offset; |
| charEnd += offsetEnd; |
| |
| // Technically that should be it, but there are 3 issues: |
| // - have to re-point column |
| // - have to re-point row |
| // - have to adjust for \n in charStart and charEnd (JAXEN issue). |
| |
| for (int i = 0, j = Math.min(exprString.length(), offset); i < j; i++) { |
| |
| if (exprString.charAt(i) == '\n') { |
| // This is a JAXEN problem/hack |
| charStart += 1; |
| |
| if (lineNo > 0) { |
| lineNo += 1; |
| } |
| columnNo = 0; |
| } |
| |
| if (columnNo >= 0) { |
| columnNo += 1; |
| } |
| } |
| |
| // Now do the same thing for charEnd |
| for (int i = 0, j = Math.min(exprString.length(), offsetEnd); i < j; i++) { |
| // This is a JAXEN problem/hack |
| if (exprString.charAt(i) == '\n') { |
| charEnd += 1; |
| } |
| } |
| |
| problem.setAttribute(IProblem.CHAR_START, charStart); |
| problem.setAttribute(IProblem.CHAR_END, charEnd); |
| |
| if (lineNo > 0) { |
| problem.setAttribute(IProblem.LINE_NUMBER, lineNo); |
| } |
| if (columnNo > 0) { |
| problem.setAttribute(IProblem.COLUMN_NUMBER, columnNo); |
| } |
| |
| } |
| |
| /** |
| * Perform static analysis of this XPath expression. |
| * |
| */ |
| @ARule( |
| sa = 0, |
| desc = "Create the static analysis visitor for expression analysis", |
| author = "michal.chmielewski@oracle.com", |
| date = "01/30/2007", |
| errors="XPATH_EXPRESSION_SYNTAX,XPATH_EXPRESSION_TYPE,XPATH_FN_ARGS,"+ |
| "XPATH_FN_LITERAL_ARGS,XPATH_UNDEF_VARIABLE,XPATH_UNRESOLVED_NAMESPACE_PREFIX"+ |
| "XPATH_INVALID_VARREF_PREFIX,XPATH_VARIABLE_PART,XPATH_UNDEF_VARIABLE_PART"+ |
| "XPATH_FUNCTION_MIN_ARGS,XPATH_FUNCTION_MAX_ARGS,XPATH_EMPTY_EXPRESSION", |
| warnings="XPATH_URI_SYNTAX,XPATH_FUNCTION_UNKNOWN,XPATH_FUNCTION_DEPRECATED,XPATH_EXPRESSION_TYPE" |
| |
| ) |
| public void rule_StaticXPathExpressionAnalysis_10() { |
| |
| mVisitor = new XPathVisitor(this); |
| |
| INode typeNode = getValue("expression.type", null); |
| |
| if (typeNode != null) { |
| mVisitor.contextPush(typeNode); |
| } |
| } |
| |
| /** |
| * Sets the value of the expression result. This is run as the last rule in |
| * pass1 and it examines the top element on the stack that the |
| * visitor/validator combination has produced. |
| * |
| */ |
| |
| public void rule_ExpressionType_999999() { |
| |
| if (mVisitor == null) { |
| return; |
| } |
| |
| Object obj = mVisitor.contextPop(); |
| |
| // Type needs to be re-mapped to XSD types that our model understands. |
| |
| if (XPathVisitor.isSimpleType(obj)) { |
| |
| setValue("expression.value", obj); |
| |
| QName typeQName = null; |
| |
| if (obj instanceof String) { |
| |
| typeQName = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, |
| "string"); |
| |
| } else if (obj instanceof Number) { |
| |
| Number num = (Number) obj; |
| |
| if (num.intValue() == num.doubleValue()) { |
| |
| if (mNode.equals(ND_START_COUNTER_VALUE) |
| || mNode.equals(ND_FINAL_COUNTER_VALUE)) { |
| if (num.intValue() >= 0) { |
| typeQName = new QName( |
| XMLConstants.W3C_XML_SCHEMA_NS_URI, |
| "unsignedInt"); |
| } |
| } |
| |
| if (typeQName == null) { |
| typeQName = new QName( |
| XMLConstants.W3C_XML_SCHEMA_NS_URI, "integer"); |
| } |
| } else { |
| typeQName = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, |
| "double"); |
| } |
| |
| } else if (obj instanceof Boolean) { |
| |
| typeQName = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, |
| "boolean"); |
| |
| } |
| |
| INode basicType = mModelQuery.lookup(mNode.rootNode(), |
| IModelQueryLookups.LOOKUP_NODE_XSD_TYPE, typeQName); |
| |
| if (isUndefined(basicType)) { |
| setValue("expression.type", null); |
| } else { |
| setValue("expression.type", basicType); |
| } |
| |
| } else if (obj instanceof INode) { |
| |
| setValue("expression.type", obj); |
| |
| } else if (obj instanceof List) { |
| |
| // node list, we can't check the types |
| |
| } else { |
| // everything else we ignore |
| } |
| |
| } |
| |
| /** |
| * Check the deadline expression. |
| */ |
| |
| public void checkDeadlineExpression() { |
| |
| IProblem problem; |
| Expr expr = xpathExpr; |
| // Bugzilla 320539: |
| if (expr instanceof UnaryExpr) { |
| expr = ((UnaryExpr) expr).getExpr(); |
| } |
| |
| if (expr instanceof LiteralExpr) { |
| LiteralExpr lexpr = (LiteralExpr) expr; |
| try { |
| ParserTool.parseDateAndTime(lexpr.getLiteral()); |
| } catch (Exception e) { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_SYNTAX", |
| toString(mNode.nodeName()), lexpr.getLiteral(), |
| fExprByNode, e.getMessage()); |
| repointOffsets(problem, expr); |
| } |
| } else if (expr instanceof FunctionCallExpr) { |
| mVisitor.visit((FunctionCallExpr) expr); |
| } else { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_TYPE", toString(mNode.nodeName()), |
| exprStringTrimmed, fExprByNode); |
| repointOffsets(problem, expr); |
| } |
| |
| disableRules(); |
| } |
| |
| /** |
| * Check the duration expression variant. |
| */ |
| |
| public void checkDurationExpression() { |
| |
| IProblem problem; |
| Expr expr = xpathExpr; |
| |
| // Bugzilla 320539: |
| if (expr instanceof UnaryExpr) { |
| expr = ((UnaryExpr) expr).getExpr(); |
| } |
| |
| if (expr instanceof LiteralExpr) { |
| LiteralExpr lexpr = (LiteralExpr) expr; |
| try { |
| ParserTool.parseDuration(lexpr.getLiteral()); |
| } catch (Exception e) { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_SYNTAX", |
| toString(mNode.nodeName()), lexpr.getLiteral(), |
| fExprByNode, e.getLocalizedMessage()); |
| repointOffsets(problem, expr); |
| } |
| } else if (expr instanceof FunctionCallExpr) { |
| mVisitor.visit((FunctionCallExpr) expr); |
| } else { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_TYPE", toString(mNode.nodeName()), |
| exprStringTrimmed, fExprByNode); |
| repointOffsets(problem, expr); |
| } |
| |
| disableRules(); |
| } |
| |
| /** |
| * Check boolean expressions |
| */ |
| |
| public void checkBooleanExpression() { |
| |
| Expr expr = xpathExpr; |
| |
| IProblem problem; |
| |
| if (expr instanceof LogicalExpr) { |
| mVisitor.visit((LogicalExpr) expr); |
| } else if (expr instanceof EqualityExpr) { |
| mVisitor.visit((EqualityExpr) expr); |
| } else if (expr instanceof RelationalExpr) { |
| mVisitor.visit((RelationalExpr) expr); |
| } else if (expr instanceof UnaryExpr |
| && !(((UnaryExpr) expr).getExpr() instanceof FunctionCallExpr)) { |
| // Bugzilla 320538: |
| // Allow unary expressions like variable references and literals |
| // Since we're using XPath 1.0 there's no XSD type checking |
| mVisitor.visit((UnaryExpr) expr); |
| } else { |
| FunctionCallExpr fce = null; |
| // Bugzilla 320535: |
| // apparently XPath functions are Unary expressions first |
| if (expr instanceof UnaryExpr |
| && ((UnaryExpr) expr).getExpr() instanceof FunctionCallExpr) { |
| fce = (FunctionCallExpr) ((UnaryExpr) expr).getExpr(); |
| } else if (expr instanceof FunctionCallExpr) { |
| fce = (FunctionCallExpr) expr; |
| } |
| |
| if (fce != null) { |
| if (isBooleanFunction(fce) == false) { |
| |
| problem = createWarning(); |
| problem.fill("XPATH_EXPRESSION_TYPE", |
| toString(mNode.nodeName()), exprStringTrimmed, |
| fExprByNode); |
| repointOffsets(problem, fce); |
| } |
| |
| mVisitor.visit(fce); |
| |
| } else { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_TYPE", |
| toString(mNode.nodeName()), exprStringTrimmed, |
| fExprByNode |
| |
| ); |
| repointOffsets(problem, expr); |
| } |
| } |
| |
| // Once validated this type of expression we are done. |
| |
| disableRules(); |
| } |
| |
| /** |
| * Check the integer expression. |
| * |
| * Not this is not a rule. |
| */ |
| |
| public void checkIntegerExpression() { |
| |
| IProblem problem; |
| Expr expr = xpathExpr; |
| |
| if (expr instanceof MultiplicativeExpr) { |
| mVisitor.visit((MultiplicativeExpr) expr); |
| } else if (expr instanceof AdditiveExpr) { |
| mVisitor.visit((AdditiveExpr) expr); |
| } else if (expr instanceof UnaryExpr) { |
| mVisitor.visit((UnaryExpr) expr); |
| } else if (expr instanceof NumberExpr) { |
| mVisitor.visit((NumberExpr) expr); |
| } else if (expr instanceof VariableReferenceExpr) { |
| mVisitor.visit((VariableReferenceExpr) expr); |
| } else { |
| problem = createError(); |
| problem.fill("XPATH_EXPRESSION_TYPE", toString(mNode.nodeName()), |
| exprStringTrimmed, fExprByNode); |
| } |
| |
| disableRules(); |
| } |
| |
| /** |
| * Check function call expression. |
| * |
| * @param expr |
| */ |
| @ARule(sa = 1015, desc = "Check functions in XPath expressions", author = "michal.chmielewski@oracle.com", date = "03/02/2007", tag = "functions", order = 1) |
| public void checkFunctions(FunctionCallExpr expr) { |
| |
| String functionPrefix = expr.getPrefix(); |
| String nsURI = lookupNamespace(functionPrefix); |
| |
| if (XPathVisitor.isBPELNS(nsURI)) { |
| runRules("bpel.functions", expr); |
| } |
| |
| IFunctionMeta meta = lookup(expr); |
| |
| checkFunctionMeta(expr, meta); |
| checkFunctionCall(expr, meta); |
| |
| } |
| |
| /** |
| * Check the GetVariableProperty function. |
| * |
| * @param expr |
| */ |
| |
| @ARule(sa = 30, desc = "Arguments to getVariableProperty must be quoted strings", author = "michal.chmielewski@oracle.com", date = "01/29/2007", tag = "bpel.functions") |
| public void checkGetVariableProperty(FunctionCallExpr expr) { |
| |
| String fn = expr.getFunctionName(); |
| if ("getVariableProperty".equals(fn) == false) { |
| return; |
| } |
| |
| List<?> params = expr.getParameters(); |
| IProblem problem; |
| |
| int psize = params.size(); |
| if (psize != 2) { |
| problem = createError(); |
| int pz = (params.size() - 2 < 0) ? 0 : 1; |
| problem.fill("XPATH_FN_ARGS", toString(mNode.nodeName()), fn, |
| expr.getText(), pz, 2); |
| repointOffsets(problem, expr); |
| |
| if (psize < 1) { |
| return; |
| } |
| } |
| |
| for (int i = 0, j = Math.min(2, params.size()); i < j; i++) { |
| Expr p = (Expr) params.get(i); |
| if ((p instanceof LiteralExpr) == false) { |
| problem = createError(); |
| problem.fill("XPATH_FN_LITERAL_ARGS", |
| toString(mNode.nodeName()), fn, expr.getText(), i + 1, |
| p.getText()); |
| |
| repointOffsets(problem, p); |
| } |
| } |
| |
| // check to see if the variable exists, it must be the 1st argument |
| Expr p1 = (Expr) params.get(0); |
| if (p1 instanceof LiteralExpr) { |
| LiteralExpr p1l = (LiteralExpr) p1; |
| |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", p1l.getLiteral()) == false) { |
| |
| INode variableNode = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NODE_VARIABLE, |
| p1l.getLiteral()); |
| |
| if (isUndefined(variableNode)) { |
| problem = createError(); |
| problem.fill("XPATH_UNDEF_VARIABLE", //$NON-NLS-1$ |
| p1l.getLiteral(), expr.getText()); |
| repointOffsets(problem, p1l); |
| } |
| } |
| } |
| |
| } |
| |
| /** |
| * @param expr |
| */ |
| @ARule(sa = 31, desc = "The second argument MUST be a string literal conforming to the definition of QName in section 3", author = "michal.chmielewski@oracle.com", date = "01/29/2007", tag = "bpel.functions") |
| public void checkGetVariableProperty2ndArgument(FunctionCallExpr expr) { |
| |
| String fn = expr.getFunctionName(); |
| |
| if ("getVariableProperty".equals(fn) == false) { |
| return; |
| } |
| |
| List<?> params = expr.getParameters(); |
| |
| if (params.size() < 2 |
| || ((params.get(1) instanceof LiteralExpr) == false)) { |
| // already handled in previous rule. |
| return; |
| } |
| |
| LiteralExpr p2l = (LiteralExpr) params.get(1); |
| |
| // Make sure that the 2nd argument is a QName |
| QName qname = mModelQuery.createQName(mNode, p2l.getLiteral()); |
| IProblem problem; |
| |
| if (isNamespaceOK(qname) == false) { |
| problem = createError(); |
| |
| problem.fill("XPATH_UNRESOLVED_NAMESPACE_PREFIX", |
| qname.getPrefix(), qname.getLocalPart()); |
| |
| repointOffsets(problem, p2l); |
| |
| return; |
| } |
| |
| // look up variable property in the model ? |
| } |
| |
| /** |
| * Check XslTransform function |
| * |
| * @param expr |
| */ |
| |
| @ARule(sa = 39, desc = "1st arguments to doXslTransform must be quoted string", author = "michal.chmielewski@oracle.com", date = "01/29/2007", tag = "bpel.functions") |
| public void CheckDoXslTransform_10(FunctionCallExpr expr) { |
| |
| String fn = expr.getFunctionName(); |
| if ("doXslTransform".equals(fn) == false) { |
| return; |
| } |
| |
| List<?> params = expr.getParameters(); |
| IProblem problem; |
| int psize = params.size(); |
| |
| if (psize < 2) { |
| |
| problem = createError(); |
| problem.fill("XPATH_FN_ARGS", toString(mNode.nodeName()), fn, |
| expr.getText(), 0, 2); |
| repointOffsets(problem, expr); |
| |
| if (psize < 1) { |
| return; |
| } |
| } |
| |
| Expr p = (Expr) params.get(0); |
| |
| if ((p instanceof LiteralExpr) == false) { |
| problem = createError(); |
| problem.fill("XPATH_FN_LITERAL_ARGS", toString(mNode.nodeName()), |
| fn, expr.getText(), 1, p.getText()); |
| |
| repointOffsets(problem, p); |
| return; |
| } |
| |
| LiteralExpr lexpr = (LiteralExpr) p; |
| |
| // quick check if this is valid URI ... |
| try { |
| new java.net.URI( lexpr.getLiteral()); |
| |
| } catch (URISyntaxException e) { |
| problem = createWarning(); |
| problem.fill("XPATH_URI_SYNTAX", toString(mNode.nodeName()), |
| lexpr.getLiteral(), expr.getText(), e.getMessage()); |
| repointOffsets(problem, lexpr); |
| } |
| |
| } |
| |
| /** |
| * @param expr |
| */ |
| @ARule(sa = 40, desc = "There must be an even number of arguments doXslTransform", author = "michal.chmielewski@oracle.com", date = "01/29/2007", tag = "bpel.functions") |
| public void CheckDoXslTransform_11(FunctionCallExpr expr) { |
| |
| String fn = expr.getFunctionName(); |
| if ("doXslTransform".equals(fn) == false) { |
| return; |
| } |
| |
| List<?> params = expr.getParameters(); |
| if (params.size() < 2) { |
| // handled in other rule |
| return; |
| } |
| |
| IProblem problem; |
| if (params.size() % 2 != 0) { |
| problem = createError(); |
| problem.fill("XPATH_FN_ARGS", toString(mNode.nodeName()), fn, |
| expr.getText(), 0, 2); |
| repointOffsets(problem, expr); |
| } |
| } |
| |
| /** |
| * Even number of arguments must be present. |
| * |
| * @param expr |
| */ |
| |
| @ARule(sa = 41, desc = "3rd,5th,7th, etc. arguments must be QName strings", author = "michal.chmielewski@oracle.com", date = "01/29/2007", tag = "bpel.functions") |
| public void CheckDoXslTransform_15(FunctionCallExpr expr) { |
| |
| String fn = expr.getFunctionName(); |
| if ("doXslTransform".equals(fn) == false) { |
| return; |
| } |
| |
| List<?> params = expr.getParameters(); |
| if (params.size() < 3) { |
| // handled in other rule |
| return; |
| } |
| |
| IProblem problem; |
| |
| // check the 3rd, 5th, 7th etc. arguments of the XPath expression to |
| // make sure |
| // that they are QNames. |
| for (int i = 2, j = params.size(); i < j; i += 2) { |
| Expr p = (Expr) params.get(i); |
| if (p instanceof LiteralExpr) { |
| LiteralExpr p2l = (LiteralExpr) p; |
| |
| QName qname = mModelQuery.createQName(mNode, p2l.getLiteral()); |
| |
| if (isNamespaceOK(qname) == false) { |
| problem = createError(); |
| problem.fill("XPATH_UNRESOLVED_NAMESPACE_PREFIX", |
| qname.getPrefix(), qname.getLocalPart()); |
| repointOffsets(problem, p); |
| } |
| } else { |
| problem = createError(); |
| problem.fill("XPATH_FN_LITERAL_ARGS", |
| toString(mNode.nodeName()), fn, expr.getText(), i + 1, |
| p.getText()); |
| repointOffsets(problem, p); |
| } |
| } |
| |
| // |
| |
| } |
| |
| /** |
| * Check variables used in XPath expressions. |
| * |
| * @param expr |
| */ |
| |
| @ARule( |
| sa = 0, |
| desc = "Check the variable namespace prefix", |
| author = "michal.chmielewski@oracle.com", |
| date = "01/30/2007", |
| tag = "variables", |
| errors="XPATH_INVALID_VARREF_PREFIX" |
| ) |
| public void rule_CheckVariable_10(VariableReferenceExpr expr) { |
| |
| if (isJoinCondition()) { |
| return; |
| } |
| |
| String prefix = expr.getPrefix(); |
| String name = expr.getVariableName(); |
| IProblem problem; |
| |
| // Bugzilla 320537: |
| // this returns too soon - only check duplicateThing if we're sure that |
| // there really IS a problem |
| // check to make sure we don't print the same message twice. |
| // if (duplicateThing("duplicate.variable.check.", name)) { |
| // return; |
| // } |
| |
| if (isEmpty(prefix) == false) { |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", name)) { |
| return; |
| } |
| problem = createError(); |
| problem.fill("XPATH_INVALID_VARREF_PREFIX", //$NON-NLS-1$ |
| prefix + ":" + name); //$NON-NLS-1$ |
| } |
| |
| int i = name.indexOf('.'); |
| String varName, partName; |
| if (i < 0) { |
| varName = name; |
| partName = null; |
| } else { |
| varName = name.substring(0, i); |
| partName = name.substring(i + 1); |
| } |
| |
| INode variable = mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_NODE_VARIABLE, varName); |
| |
| if (isUndefined(variable)) { |
| |
| // Bugzilla 320537: |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", name)) { |
| return; |
| } |
| problem = createError(); |
| problem.fill("XPATH_UNDEF_VARIABLE", //$NON-NLS-1$ |
| varName, expr.getText()); |
| |
| repointOffsets(problem, expr); |
| |
| // if it does not exist, we are done |
| mVisitor.contextPush(Collections.EMPTY_LIST); |
| return; |
| } |
| |
| // Get the type reference of the variable |
| INode varTypeNode = getValue(variable, "type", null); |
| |
| // If the variable has problems, leave now |
| if (hasProblems(variable) || isUndefined(varTypeNode)) { |
| mVisitor.contextPush(Collections.EMPTY_LIST); |
| return; |
| } |
| |
| mVisitor.contextPush(Collections.EMPTY_LIST); |
| |
| // check variable parts |
| INode variablePart = null; |
| |
| if (partName != null) { |
| |
| variablePart = mModelQuery.lookup(varTypeNode, |
| IModelQueryLookups.LOOKUP_NODE_TYPE_OF_PART, partName); |
| } |
| |
| if (partName != null) { |
| |
| if (WSDL_ND_MESSAGE.equals(varTypeNode.nodeName()) == false) { |
| // Bugzilla 320537: |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", name)) { |
| return; |
| } |
| problem = createError(); |
| problem.fill("XPATH_VARIABLE_PART", varName, partName, |
| expr.getText(), 0); |
| repointOffsets(problem, expr); |
| |
| } else if (isUndefined(variablePart)) { |
| |
| // Bugzilla 320537: |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", name)) { |
| return; |
| } |
| problem = createError(); |
| problem.fill("XPATH_UNDEF_VARIABLE_PART", //$NON-NLS-1$ |
| varName, partName, expr.getText()); |
| repointOffsets(problem, expr); |
| |
| } else { |
| mVisitor.contextPop(); |
| mVisitor.contextPush(variablePart); |
| } |
| |
| } else { |
| // there is no part name specified, but variable does have more then |
| // 1 part. |
| if (WSDL_ND_MESSAGE.equals(varTypeNode.nodeName())) { |
| // Bugzilla 320537: |
| // check to make sure we don't print the same message twice. |
| if (duplicateThing("duplicate.variable.check.", name)) { |
| return; |
| } |
| problem = createError(); |
| problem.fill("XPATH_VARIABLE_PART", varName, |
| "text.term.unspecified", expr.getText(), 1); |
| repointOffsets(problem, expr); |
| |
| } else { |
| mVisitor.contextPop(); |
| mVisitor.contextPush(varTypeNode); |
| } |
| } |
| |
| } |
| |
| boolean isNamespaceOK(QName qname) { |
| if (qname == null) { |
| return false; |
| } |
| return (isEmpty(qname.getNamespaceURI()) == isEmpty(qname.getPrefix())); |
| } |
| |
| protected boolean isJoinCondition() { |
| /** ND_ are QNames, so make sure you compare apples to apples ... */ |
| return ND_JOIN_CONDITION.equals(mNode.nodeName()); |
| } |
| |
| protected IModelQuery getModelQuery() { |
| return mModelQuery; |
| } |
| |
| protected INode getNode() { |
| return mNode; |
| } |
| |
| /** |
| * This exists here for visibility to XPathVisitor. |
| * |
| * @see org.eclipse.bpel.validator.model.Validator#createError() |
| */ |
| |
| @Override |
| protected IProblem createError() { |
| return super.createError(); |
| } |
| |
| /** |
| * This exists here for visibility to XPathVisitor. |
| * |
| * @see org.eclipse.bpel.validator.model.Validator#createInfo() |
| */ |
| @Override |
| protected IProblem createInfo() { |
| return super.createInfo(); |
| } |
| |
| /** |
| * This exists here for visibility to XPathVisitor. |
| * |
| * @see org.eclipse.bpel.validator.model.Validator#createWarning() |
| */ |
| @Override |
| protected IProblem createWarning() { |
| return super.createWarning(); |
| } |
| |
| /** |
| * @see org.eclipse.bpel.validator.model.Validator#runRules(java.lang.String, |
| * java.lang.Object[]) |
| */ |
| |
| @Override |
| protected void runRules(String tag, Object... args) { |
| // this exists here for visibility to XPathVisitor. |
| super.runRules(tag, args); |
| } |
| |
| protected boolean duplicateThing(String... args) { |
| |
| String key = null; |
| if (args.length == 1) { |
| key = args[0]; |
| } else { |
| StringBuilder sb = new StringBuilder(); |
| for (String a : args) { |
| sb.append(a); |
| } |
| key = sb.toString(); |
| } |
| |
| if (containsValueKey(key)) { |
| return true; |
| } |
| |
| setValue(key, Boolean.TRUE); |
| return false; |
| } |
| |
| protected boolean checkPrefix(String prefix, String name) { |
| |
| if (isEmptyOrWhitespace(prefix)) { |
| return true; |
| } |
| |
| String nsURI = lookupNamespace(prefix); |
| |
| if (isEmpty(nsURI)) { |
| IProblem problem = createError(); |
| problem.fill("XPATH_UNRESOLVED_NAMESPACE_PREFIX", //$NON-NLS-1$ |
| prefix, name); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| protected String lookupNamespace(String prefix) { |
| return mModelQuery.lookup(mNode, |
| IModelQueryLookups.LOOKUP_TEXT_PREFIX2NS, prefix, null); |
| } |
| |
| /** |
| * Check if the function is a boolean function. |
| * |
| * @param functionExpr |
| * @return true if a function is boolean, false otherwise. |
| */ |
| boolean isBooleanFunction(FunctionCallExpr functionExpr) { |
| |
| IFunctionMeta meta = lookup(functionExpr); |
| if (meta == null) { |
| return false; |
| } |
| return meta.getReturnType() == Boolean.class; |
| } |
| |
| /** |
| * Empty prefix in functionExpr implies an XPath function. |
| * |
| * @param functionExpr |
| */ |
| protected void checkFunctionMeta(FunctionCallExpr functionExpr, |
| IFunctionMeta meta) { |
| |
| String fnCall = functionExpr.getFunctionName(); |
| if (isEmptyOrWhitespace(functionExpr.getPrefix()) == false) { |
| fnCall = functionExpr.getPrefix() + ":" + fnCall; |
| } |
| |
| if (duplicateThing("function.meta.", fnCall)) { |
| return; |
| } |
| |
| IProblem problem; |
| |
| if (meta == null) { |
| problem = createWarning(); |
| problem.fill("XPATH_FUNCTION_UNKNOWN", //$NON-NLS-1$ |
| toString(mNode.nodeName()), fnCall); |
| repointOffsets(problem, functionExpr); |
| |
| return; |
| } |
| |
| if (meta.isDeprecated()) { |
| problem = createWarning(); |
| problem.fill( |
| "XPATH_FUNCTION_DEPRECATED", //$NON-NLS-1$ |
| toString(mNode.nodeName()), fnCall, |
| meta.getDeprecateComment()); |
| repointOffsets(problem, functionExpr); |
| } |
| } |
| |
| protected void checkFunctionCall(FunctionCallExpr functionExpr, |
| IFunctionMeta meta) { |
| |
| if (meta == null || functionExpr == null) { |
| return; |
| } |
| |
| String fnCall = functionExpr.getFunctionName(); |
| if (isEmptyOrWhitespace(functionExpr.getPrefix()) == false) { |
| fnCall = functionExpr.getPrefix() + ":" + fnCall; |
| } |
| |
| IProblem problem; |
| |
| List<?> params = functionExpr.getParameters(); |
| if (params.size() < meta.getMinArity()) { |
| problem = createError(); |
| problem.fill( |
| "XPATH_FUNCTION_MIN_ARGS", //$NON-NLS-1$ |
| toString(mNode.nodeName()), fnCall, meta.getMinArity(), |
| params.size()); |
| repointOffsets(problem, functionExpr); |
| } |
| |
| if (params.size() > meta.getMaxArity()) { |
| problem = createError(); |
| problem.fill( |
| "XPATH_FUNCTION_MAX_ARGS", //$NON-NLS-1$ |
| toString(mNode.nodeName()), fnCall, meta.getMaxArity(), |
| params.size()); |
| } |
| |
| } |
| |
| protected IFunctionMeta lookup(FunctionCallExpr functionExpr) { |
| |
| String ns = null; |
| if (isEmptyOrWhitespace(functionExpr.getPrefix())) { |
| ns = IConstants.XMLNS_XPATH_EXPRESSION_LANGUAGE; |
| } else { |
| ns = lookupNamespace(functionExpr.getPrefix()); |
| } |
| return mModelQuery.lookupFunction( |
| IConstants.XMLNS_XPATH_EXPRESSION_LANGUAGE, ns, |
| functionExpr.getFunctionName()); |
| } |
| |
| } |