net.sf.saxon.om.AbsolutePath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.om;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.trans.Err;
import net.sf.saxon.tree.util.Navigator;
import net.sf.saxon.type.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* Represents the path from the root of an XDM tree to a specific node, as a sequence of (name, position) pairs
*/
public class AbsolutePath {
private final List path;
private String systemId;
/**
* Create an absolute path given a list of path elements
*
* @param path the list of path elements, starting from the root. It is not necessary to include a path element
* for the document node.
*/
public AbsolutePath(Collection path) {
this.path = new ArrayList<>(path);
}
/**
* Append an attribute name to the path
* @param attributeName the name of the attribute
*/
public void appendAttributeName(NodeName attributeName) {
if (!path.isEmpty()) {
PathElement last = path.get(path.size()-1);
if (last.getNodeKind() == Type.ATTRIBUTE) {
path.remove(path.size()-1);
}
}
PathElement att = new PathElement(Type.ATTRIBUTE, attributeName, 1);
path.add(att);
}
/**
* Create an absolute path given a Node
* @param node the node whose path is required
* @return the absolute path
*/
public static AbsolutePath pathToNode(NodeInfo node) {
LinkedList list = new LinkedList<>();
while (node != null && node.getNodeKind() != Type.DOCUMENT) {
PathElement pe = new PathElement(node.getNodeKind(), NameOfNode.makeName(node), Navigator.getNumberSimple(node, null));
list.addFirst(pe);
node = node.getParent();
}
return new AbsolutePath(list);
}
/**
* Get a string representing the path using namespace prefixes to represent QNames
*
* @return the path in the form /prefix:local[n]/prefix:local[m]/...
*/
public String getPathUsingPrefixes() {
StringBuilder fsb = new StringBuilder(256);
for (AbsolutePath.PathElement pe : path) {
fsb.append('/');
pe.addToString(fsb, 'p');
}
return fsb.toString();
}
/**
* Get a string representing the path using namespace URIs to represent QNames
*
* @return the path in the form /Q{uri}local[n]/Q{uri}local[m]/...
*/
public String getPathUsingUris() {
StringBuilder fsb = new StringBuilder(256);
for (AbsolutePath.PathElement pe : path) {
fsb.append('/');
pe.addToString(fsb, 'u');
}
return fsb.toString();
}
/**
* Get a string representing the path using abbreviated namespace URIs to represent QNames
*
* @return the path in the form /Q{uri}local[n]/Q{uri}local[m]/...
, with the URIs shortened
*/
public String getPathUsingAbbreviatedUris() {
StringBuilder fsb = new StringBuilder(256);
for (AbsolutePath.PathElement pe : path) {
fsb.append('/');
pe.addToString(fsb, 's');
}
return fsb.toString();
}
@Override
public String toString() {
return getPathUsingUris();
}
@Override
public boolean equals(Object obj) {
return obj instanceof AbsolutePath && obj.toString().equals(toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
public void setSystemId(String systemId) {
this.systemId = systemId;
}
public String getSystemId() {
return systemId;
}
/**
* Inner class representing one step in the path
*/
public static class PathElement {
int nodeKind;
NodeName name;
int index;
/**
* Create a path element
*
* @param nodeKind the kind of node
* @param name the name of the node
* @param index the position of the node relative to siblings of the same node kind and name.
* The value -1 indicates "not known", which will typically be the case for streamed nodes.
*/
public PathElement(int nodeKind, NodeName name, int index) {
this.nodeKind = nodeKind;
this.name = name;
this.index = index;
}
/**
* Get the node kind
*
* @return the node kind, as a constant from {@link net.sf.saxon.type.Type}
*/
public int getNodeKind() {
return nodeKind;
}
/**
* Get the name of the node
*
* @return the node name
*/
public NodeName getName() {
return name;
}
/**
* Get the position of the node
*
* @return the position relative to siblings of the same node kind and name.
* The value -1 indicates "not known", which will typically be the case for streamed nodes.
*/
public int getIndex() {
return index;
}
/**
* Get a string representation of the path
*
* @param fsb buffer into which the string representation will be written
* @param option for representing namespaces:
* 'p': use namepace prefix. 'u': use full URI. 's': use abbreviated URI
*/
public void addToString(StringBuilder fsb, char option) {
switch (nodeKind) {
case Type.DOCUMENT:
fsb.append("(/)");
break;
case Type.ATTRIBUTE:
fsb.append('@');
if (!name.getURI().isEmpty()) {
if (option == 'u') {
fsb.append("Q{");
fsb.append(name.getURI());
fsb.append("}");
} else if (option == 'p') {
String prefix = name.getPrefix();
if (!prefix.isEmpty()) {
fsb.append(prefix);
fsb.append(':');
}
} else if (option == 's') {
fsb.append("Q{");
fsb.append(Err.abbreviateURI(name.getURI()));
fsb.append("}");
}
}
fsb.append(getName().getLocalPart());
break;
case Type.ELEMENT:
if (option == 'u') {
fsb.append("Q{");
fsb.append(name.getURI());
fsb.append("}");
} else if (option == 'p') {
String prefix = name.getPrefix();
if (!prefix.isEmpty()) {
fsb.append(prefix);
fsb.append(':');
}
} else if (option == 's') {
if (!name.getURI().isEmpty()) {
fsb.append("Q{");
fsb.append(Err.abbreviateURI(name.getURI()));
fsb.append("}");
}
}
fsb.append(name.getLocalPart());
appendPredicate(fsb);
break;
case Type.TEXT:
fsb.append("text()");
break;
case Type.COMMENT:
fsb.append("comment()");
appendPredicate(fsb);
break;
case Type.PROCESSING_INSTRUCTION:
fsb.append("processing-instruction(");
fsb.append(name.getLocalPart());
fsb.append(")");
appendPredicate(fsb);
break;
case Type.NAMESPACE:
fsb.append("namespace::");
if (name.getLocalPart().isEmpty()) {
fsb.append("*[Q{" + NamespaceConstant.FN + "}local-name()=\"\"]");
} else {
fsb.append(name.getLocalPart());
}
break;
default:
}
}
private void appendPredicate(StringBuilder fsb) {
int index = getIndex();
if (index != -1) {
fsb.append('[');
fsb.append(getIndex() + "");
fsb.append(']');
}
}
}
}