org.apache.xmlbeans.impl.xpathgen.XPathGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.xpathgen;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlCursor.TokenType;
import org.apache.xmlbeans.XmlObject;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
/**
* Generates an XPath String that points to a given position in an XML document
*/
public class XPathGenerator
{
/**
* Generates an XPath pointing to the position in the document indicated by {@code node}.
* If the {@code context} parameter is null, the XPath is absolute, otherwise the
* XPath will be relative to the position indicated by {@code context}.
* Note: the cursor position for the {@code node} parameter is not preserved
* @param node the position in the document that the generated path will point to
* @param context the context node; the generated path will be relative to it if not null and if
* pointing to an element on the path from the document root to {@code node}
* @param nsctx a namespace context that will be used to obtain prefixes; a (non-default)
* namespace mapping must be available for all required namespace URIs
* @return the generated path as a {@code String}
* @throws XPathGenerationException if the path could not be generated: the cursor is in a bad
* position (like over a comment) or no prefix mapping was found for one of the namespace URIs
*/
public static String generateXPath(XmlCursor node, XmlCursor context, NamespaceContext nsctx)
throws XPathGenerationException
{
if (node == null)
throw new IllegalArgumentException("Null node");
if (nsctx == null)
throw new IllegalArgumentException("Null namespace context");
TokenType tt = node.currentTokenType();
if (context != null && node.isAtSamePositionAs(context))
return ".";
switch (tt.intValue())
{
case TokenType.INT_ATTR:
QName name = node.getName();
node.toParent();
String pathToParent = generateInternal(node, context, nsctx);
return pathToParent + '/' + '@' + qnameToString(name, nsctx);
case TokenType.INT_NAMESPACE:
name = node.getName();
node.toParent();
pathToParent = generateInternal(node, context, nsctx);
String prefix = name.getLocalPart();
if (prefix.length() == 0)
return pathToParent + "/@xmlns";
else
return pathToParent + "/@xmlns:" + prefix;
case TokenType.INT_START:
case TokenType.INT_STARTDOC:
return generateInternal(node, context, nsctx);
case TokenType.INT_TEXT:
int nrOfTextTokens = countTextTokens(node);
node.toParent();
pathToParent = generateInternal(node, context, nsctx);
if (nrOfTextTokens == 0)
return pathToParent + "/text()";
else
return pathToParent + "/text()[position()=" + nrOfTextTokens + ']';
default:
throw new XPathGenerationException("Cannot generate XPath for cursor position: " +
tt.toString());
}
}
private static String generateInternal(XmlCursor node, XmlCursor context, NamespaceContext nsctx)
throws XPathGenerationException
{
if (node.isStartdoc())
return "";
if (context != null && node.isAtSamePositionAs(context))
return ".";
assert node.isStart();
QName name = node.getName();
int elemIndex = 0, i = 1;
try (XmlCursor d = node.newCursor()) {
if (!node.toParent())
return "/" + name;
node.push();
if (!node.toChild(name))
throw new IllegalStateException("Must have at least one child with name: " + name);
do
{
if (node.isAtSamePositionAs(d))
elemIndex = i;
else
i++;
} while (node.toNextSibling(name));
node.pop();
}
String pathToParent = generateInternal(node, context, nsctx);
return i == 1 ? pathToParent + '/' + qnameToString(name, nsctx) :
pathToParent + '/' + qnameToString(name, nsctx) + '[' + elemIndex + ']';
}
private static String qnameToString(QName qname, NamespaceContext ctx)
throws XPathGenerationException
{
String localName = qname.getLocalPart();
String uri = qname.getNamespaceURI();
if (uri.length() == 0)
return localName;
String prefix = qname.getPrefix();
if (prefix != null && prefix.length() > 0)
{
// Try to use the same prefix if it maps to the right URI
String mappedUri = ctx.getNamespaceURI(prefix);
if (uri.equals(mappedUri))
return prefix + ':' + localName;
}
// The prefix is not specified, or it is not mapped to the right URI
prefix = ctx.getPrefix(uri);
if (prefix == null)
throw new XPathGenerationException("Could not obtain a prefix for URI: " + uri);
if (prefix.length() == 0)
throw new XPathGenerationException("Can not use default prefix in XPath for URI: " + uri);
return prefix + ':' + localName;
}
/**
* Computes how many text nodes the
* @param c the position in the document
* @return how many text nodes occur before the position determined by {@code c}
*/
private static int countTextTokens(XmlCursor c)
{
int k = 0;
int l = 0;
try (XmlCursor d = c.newCursor()) {
c.push();
c.toParent();
TokenType tt = c.toFirstContentToken();
while (!tt.isEnd())
{
if (tt.isText())
{
if (c.comparePosition(d) > 0)
// We have moved after the initial position
l++;
else
k++;
}
else if (tt.isStart())
c.toEndToken();
tt = c.toNextToken();
}
}
c.pop();
return l == 0 ? 0 : k;
}
public static void main(String[] args) throws org.apache.xmlbeans.XmlException
{
String xml =
"\n" +
"text1 text2 text3text text4 \n" +
" ";
NamespaceContext ns = new NamespaceContext() {
public String getNamespaceURI(String prefix)
{
if ("ns".equals(prefix))
return "http://a.com";
else
return null;
}
public String getPrefix(String namespaceUri)
{
return null;
}
public java.util.Iterator getPrefixes(String namespaceUri)
{
return null;
}
};
try (XmlCursor c = XmlObject.Factory.parse(xml).newCursor()) {
c.toFirstContentToken(); // on
c.toFirstContentToken(); // on
c.toFirstChild(); // on
c.toFirstChild(); // on
c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
c.toNextSibling();
c.toNextSibling(); // on the last
c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
try (XmlCursor d = c.newCursor()) {
d.toParent();
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
d.toParent();
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
c.toFirstContentToken(); // on text content of the last
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
c.toParent();
c.toPrevToken(); // on text content before the last
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
c.toParent(); // on
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
c.toFirstAttribute(); // on the "foo" attribute
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
c.toParent();
c.toParent();
c.toNextToken(); // on the "xmlns:ns" attribute
c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
}
c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy