diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index 86041d65cc..3626563d24 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -12,7 +12,9 @@ aliases: type: "xs:element" description: "Any element node" - &needs_typenode "The context node must be a {% jdoc jast::TypeNode %}" - - ¬_ctx_dependent "The function is not context-dependent, but takes a node as its first parameter." + - &coord_fun_note | + The function is not context-dependent, but takes a node as its first parameter. + The function is only available in XPath 2.0. - &needs_node_ctx "The requires the context node to be an element" langs: @@ -32,19 +34,19 @@ langs: - code: "//b[pmd:fileName() = 'Foo.xml']" outcome: "Matches any `<b>` tags in files called `Foo.xml`." - - name: beginLine + - name: startLine returnType: "xs:int" parameters: - *node_param - shortDescription: "Returns the begin line of the given node" + shortDescription: "Returns the start line of the given node" description: | Returns the line where the node starts in the source file. Line numbers are 1-based. since: 6.44.0 - notes: *not_ctx_dependent + notes: *coord_fun_note examples: - - code: "//b[pmd:beginLine(.) > 5]" + - code: "//b[pmd:startLine(.) > 5]" outcome: "Matches any `<b>` node which starts after the fifth line." - name: endLine @@ -57,11 +59,40 @@ langs: Line numbers are 1-based. since: 6.44.0 - notes: *not_ctx_dependent + notes: *coord_fun_note examples: - - code: "//b[pmd:endLine(.) == pmd:beginLine(.)]" + - code: "//b[pmd:endLine(.) == pmd:startLine(.)]" outcome: "Matches any `<b>` node which doesn't span more than one line." + - name: startColumn + returnType: "xs:int" + parameters: + - *node_param + shortDescription: "Returns the start column of the given node (inclusive)" + description: | + Returns the column number where the node starts in the source file. + Column numbers are 1-based. The start column is inclusive. + + since: 6.44.0 + notes: *coord_fun_note + examples: + - code: "//b[pmd:startColumn(.) = 1]" + outcome: "Matches any `<b>` node which starts on the first column of a line" + + - name: endColumn + returnType: "xs:int" + parameters: + - *node_param + shortDescription: "Returns the end column of the given node (exclusive)" + description: | + Returns the column number where the node ends in the source file. + Column numbers are 1-based. The end column is exclusive. + + since: 6.44.0 + notes: *coord_fun_note + examples: + - code: "//b[pmd:startLine(.) = pmd:endLine(.) and pmd:endColumn(.) - pmd:startColumn(.) = 1]" + outcome: "Matches any `<b>` node which spans exactly one character" - name: "Java" ns: "pmd-java" diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/PMDFunctions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/PMDFunctions.java index 5fb1a48c15..e4d99dcd4a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/PMDFunctions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/xpath/PMDFunctions.java @@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode; import net.sf.saxon.dom.NodeWrapper; import net.sf.saxon.expr.XPathContext; import net.sf.saxon.om.Item; +import net.sf.saxon.om.NodeInfo; @InternalApi @@ -61,16 +62,26 @@ public final class PMDFunctions { return node == null ? null : FileNameXPathFunction.getFileName(node); } - public static int beginLine(Item item) { + public static int startLine(NodeInfo item) { Node node = Objects.requireNonNull(itemToNode(item), "not a node " + item); return node.getBeginLine(); } - public static int endLine(Item item) { + public static int endLine(NodeInfo item) { Node node = Objects.requireNonNull(itemToNode(item), "not a node " + item); return node.getEndLine(); } + public static int startColumn(NodeInfo item) { + Node node = Objects.requireNonNull(itemToNode(item), "not a node " + item); + return node.getBeginColumn(); + } + + public static int endColumn(NodeInfo item) { + Node node = Objects.requireNonNull(itemToNode(item), "not a node " + item); + return node.getEndColumn() + 1; // exclusive + } + private static Node itemToNode(Object item) { if (item instanceof Node) { return (Node) item;