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 commons-xmlbeans Show documentation
Show all versions of commons-xmlbeans Show documentation
The Apache Commons Codec package contains simple encoder and decoders for
various formats such as Base64 and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
The newest version!
/* 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 javax.xml.namespace.QName;
import javax.xml.namespace.NamespaceContext;
/**
* 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 node
.
* If the context
parameter is null, the XPath is absolute, otherwise the
* XPath will be relative to the position indicated by context
.
* Note: the cursor position for the 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 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 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();
XmlCursor d = node.newCursor();
if (!node.toParent())
return "/" + name;
int elemIndex = 0, i = 1;
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();
d.dispose();
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 c
*/
private static int countTextTokens(XmlCursor c)
{
int k = 0;
int l = 0;
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;
}
};
XmlCursor c = org.apache.xmlbeans.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();
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();
}
}