use of org.exist.dom.persistent.NodeProxy in project exist by eXist-db.
the class DOMFile method findValue.
/**
* Retrieve node at virtual address.
*
* @param broker the database broker
* @param node The virtual address
* @return The reference of the node
* @throws IOException if an I/O error occurs
* @throws BTreeException if an error occurs reading the tree
*/
protected long findValue(final DBBroker broker, final NodeProxy node) throws IOException, BTreeException {
if (LOG.isDebugEnabled() && !lockManager.isBtreeLocked(getLockName())) {
LOG.debug("The file doesn't own a lock");
}
final DocumentImpl doc = node.getOwnerDocument();
final NodeRef nodeRef = new NativeBroker.NodeRef(doc.getDocId(), node.getNodeId());
// first try to find the node in the index
final long pointer = findValue(nodeRef);
if (pointer == KEY_NOT_FOUND) {
// node not found in index: try to find the nearest available
// ancestor and traverse it
NodeId nodeID = node.getNodeId();
long parentPointer = KEY_NOT_FOUND;
do {
nodeID = nodeID.getParentId();
if (nodeID == null) {
SanityCheck.TRACE("Node " + node.getOwnerDocument().getDocId() + ":<null nodeID> not found.");
throw new BTreeException("Node not found.");
}
if (nodeID == NodeId.DOCUMENT_NODE) {
SanityCheck.TRACE("Node " + node.getOwnerDocument().getDocId() + ":" + nodeID + " not found.");
return KEY_NOT_FOUND;
}
final NativeBroker.NodeRef parentRef = new NativeBroker.NodeRef(doc.getDocId(), nodeID);
try {
parentPointer = findValue(parentRef);
} catch (final BTreeException bte) {
LOG.error("report me", bte);
}
} while (parentPointer == KEY_NOT_FOUND);
try {
final int thisLevel = nodeID.getTreeLevel();
// lazily initialized below
Integer childLevel = null;
final NodeProxy parent = new NodeProxy(doc, nodeID, parentPointer);
final EmbeddedXMLStreamReader reader = (EmbeddedXMLStreamReader) broker.getXMLStreamReader(parent, true);
while (reader.hasNext()) {
final int status = reader.next();
if (status != XMLStreamReader.END_ELEMENT) {
if (childLevel == null) {
childLevel = reader.getNode().getNodeType() == Node.ELEMENT_NODE ? thisLevel + 1 : thisLevel;
}
final NodeId otherId = (NodeId) reader.getProperty(ExtendedXMLStreamReader.PROPERTY_NODE_ID);
if (otherId.equals(node.getNodeId())) {
return reader.getCurrentPosition();
}
}
if (status == XMLStreamConstants.END_ELEMENT) {
final NodeId otherId = (NodeId) reader.getProperty(ExtendedXMLStreamReader.PROPERTY_NODE_ID);
final int otherLevel = otherId.getTreeLevel();
if (childLevel != null && childLevel != otherLevel && otherLevel == thisLevel) {
// exit-while
break;
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Node {} could not be found. Giving up. This is usually not an error.", node.getNodeId());
}
return KEY_NOT_FOUND;
} catch (final XMLStreamException e) {
SanityCheck.TRACE("Node " + node.getOwnerDocument().getDocId() + ":" + node.getNodeId() + " not found.");
throw new BTreeException("Node " + node.getNodeId() + " not found.");
}
} else {
return pointer;
}
}
use of org.exist.dom.persistent.NodeProxy in project exist by eXist-db.
the class DOMFile method getNodeValue.
/**
* Retrieve the string value of the specified node. This is an optimized low-level method
* which will directly traverse the stored DOM nodes and collect the string values of
* the specified root node and all its descendants. By directly scanning the stored
* node data, we do not need to create a potentially large amount of node objects
* and thus save memory and time for garbage collection.
*
* @param broker the database broker
* @param node the node
* @param addWhitespace true if whitespace should be added to the node value
* @return string value of the specified node
*/
public String getNodeValue(final DBBroker broker, final IStoredNode node, final boolean addWhitespace) {
if (LOG.isDebugEnabled() && !lockManager.isBtreeLocked(getLockName())) {
LOG.debug("The file doesn't own a lock");
}
try {
long address = node.getInternalAddress();
RecordPos recordPos = null;
// try to directly locate the root node through its storage address
if (StorageAddress.hasAddress(address)) {
recordPos = findRecord(address);
}
if (recordPos == null) {
// fallback to a BTree lookup if the node could not be found
// by its storage address
address = findValue(broker, new NodeProxy(node));
if (address == BTree.KEY_NOT_FOUND) {
LOG.error("Node value not found: {}", node);
// TODO : throw exception ? -pb
return null;
}
recordPos = findRecord(address);
SanityCheck.THROW_ASSERT(recordPos != null, "Node data could not be found!");
// TODO : throw exception ? -pb
}
// we collect the string values in binary format and append them to a ByteArrayOutputStream
try (final UnsynchronizedByteArrayOutputStream os = new UnsynchronizedByteArrayOutputStream(32)) {
// now traverse the tree
getNodeValue(broker.getBrokerPool(), os, recordPos, true, addWhitespace);
final byte[] data = os.toByteArray();
final XMLString str = UTF8.decode(data);
if (str != null) {
return str.toString();
} else {
return "";
}
}
} catch (final BTreeException e) {
LOG.error("BTree error while reading node value", e);
// TODO : rethrow exception ? -pb
} catch (final Exception e) {
LOG.error("IO error while reading node value", e);
// TODO : rethrow exception ? -pb
}
// TODO : remove if exceptions thrown...
return null;
}
use of org.exist.dom.persistent.NodeProxy in project exist by eXist-db.
the class RootNode method eval.
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().start(this);
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
if (contextItem != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
}
}
// first, if we have been explicitly given a context item or context sequence, we can just use that
if (contextItem != null) {
return new ValueSequence(contextItem);
} else if (contextSequence != null && contextSequence != Sequence.EMPTY_SEQUENCE) {
return contextSequence;
}
// second, check if a context item is declared
final ContextItemDeclaration decl = context.getContextItemDeclartion();
if (decl != null) {
final Sequence seq = decl.eval(null, null);
if (!seq.isEmpty()) {
final Item item = seq.itemAt(0);
// context item must be a node
if (!Type.subTypeOf(item.getType(), Type.NODE)) {
throw new XPathException(this, ErrorCodes.XPTY0020, "Context item is not a node");
}
final NodeValue node = (NodeValue) item;
// return fn:root(self::node()) treat as document-node()
if (node.getImplementationType() == NodeValue.PERSISTENT_NODE) {
return new NodeProxy(((NodeProxy) item).getOwnerDocument());
} else {
if (node.getType() == Type.DOCUMENT) {
return node;
}
return (org.exist.dom.memtree.DocumentImpl) node.getOwnerDocument();
}
}
return Sequence.EMPTY_SEQUENCE;
}
// get statically known documents from the context
DocumentSet ds = context.getStaticallyKnownDocuments();
if (ds == null || ds.getDocumentCount() == 0) {
return Sequence.EMPTY_SEQUENCE;
}
// fix for util:eval-with-context
if (contextSequence != null) {
if (!contextSequence.isEmpty()) {
final Item item = contextSequence.itemAt(0);
// context item must be a node
if (!Type.subTypeOf(item.getType(), Type.NODE)) {
throw new XPathException(this, ErrorCodes.XPTY0020, "Context item is not a node");
}
final NodeValue node = (NodeValue) item;
// return fn:root(self::node()) treat as document-node()
if (node.getImplementationType() == NodeValue.PERSISTENT_NODE) {
return new NodeProxy(((NodeProxy) item).getOwnerDocument());
} else {
if (node.getType() == Type.DOCUMENT) {
return node;
}
return (org.exist.dom.memtree.DocumentImpl) node.getOwnerDocument();
}
} else {
return Sequence.EMPTY_SEQUENCE;
}
}
// // if the expression occurs in a nested context, we might have cached the
// // document set
// // TODO: disabled cache for now as it may cause concurrency issues
// // better use compile-time inspection and maybe a pragma to mark those
// // sections in the query that can be safely cached
// if (cachedDocs != null && cachedDocs.equalDocs(ds)) return cached;
// check if the loaded documents should remain locked
NewArrayNodeSet result = new NewArrayNodeSet();
// NOTE(AR) locking the documents here does not actually do anything useful, eXist-db will still exhibit weak isolation with concurrent updates
// ManagedLocks<ManagedDocumentLock> docLocks = null;
// try {
// // wait for pending updates
// if (!context.inProtectedMode()) {
// docLocks = ds.lock(context.getBroker(), false);
// }
DocumentImpl doc;
for (final Iterator<DocumentImpl> i = ds.getDocumentIterator(); i.hasNext(); ) {
doc = i.next();
if (context.inProtectedMode() && !context.getProtectedDocs().containsKey(doc.getDocId())) {
continue;
}
if (doc.getResourceType() == DocumentImpl.XML_FILE) {
// skip binary resources
result.add(new NodeProxy(doc));
}
}
cached = result;
cachedDocs = ds;
// result.updateNoSort();
if (context.getProfiler().isEnabled()) {
context.getProfiler().end(this, "", result);
}
registerUpdateListener();
return result;
}
use of org.exist.dom.persistent.NodeProxy in project exist by eXist-db.
the class Predicate method selectByPosition.
/**
* @param outerSequence the outer sequence
* @param contextSequence the context sequence
* @param mode the mode
* @param innerSeq the inner sequence
*
* @return The result of the positional evaluation of the predicate.
*
* @throws XPathException if an error occurs
*/
private Sequence selectByPosition(final Sequence outerSequence, final Sequence contextSequence, final int mode, final Sequence innerSeq) throws XPathException {
if (outerSequence != null && !outerSequence.isEmpty() && Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && contextSequence.isPersistentSet() && outerSequence.isPersistentSet()) {
final Sequence result = new NewArrayNodeSet();
final NodeSet contextSet = contextSequence.toNodeSet();
switch(mode) {
case Constants.CHILD_AXIS:
case Constants.ATTRIBUTE_AXIS:
case Constants.DESCENDANT_AXIS:
case Constants.DESCENDANT_SELF_AXIS:
case Constants.DESCENDANT_ATTRIBUTE_AXIS:
{
final NodeSet outerNodeSet = outerSequence.toNodeSet();
// TODO: in some cases, especially with in-memory nodes,
// outerSequence.toNodeSet() will generate a document
// which will be different from the one(s) in contextSet
// ancestors will thus be empty :-(
// A special treatment of VirtualNodeSet does not seem to be
// required anymore
final Sequence ancestors = outerNodeSet.selectAncestors(contextSet, true, getExpressionId());
if (contextSet.getDocumentSet().intersection(outerNodeSet.getDocumentSet()).getDocumentCount() == 0) {
LOG.info("contextSet and outerNodeSet don't share any document");
}
final NewArrayNodeSet temp = new NewArrayNodeSet();
for (final SequenceIterator i = ancestors.iterate(); i.hasNext(); ) {
NodeProxy p = (NodeProxy) i.nextItem();
ContextItem contextNode = p.getContext();
temp.reset();
while (contextNode != null) {
if (contextNode.getContextId() == getExpressionId()) {
temp.add(contextNode.getNode());
}
contextNode = contextNode.getNextDirect();
}
p.clearContext(getExpressionId());
// TODO : understand why we sort here...
temp.sortInDocumentOrder();
for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
final NumericValue v = (NumericValue) j.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
// ... whereas we don't want a sorted array here
// TODO : rename this method as getInDocumentOrder ? -pb
p = temp.get(v.getInt() - 1);
if (p != null) {
result.add(p);
}
// TODO : does null make sense here ? Well... sometimes ;-)
}
}
}
break;
}
default:
for (final SequenceIterator i = outerSequence.iterate(); i.hasNext(); ) {
NodeProxy p = (NodeProxy) i.nextItem();
Sequence temp;
boolean reverseAxis = true;
switch(mode) {
case Constants.ANCESTOR_AXIS:
temp = contextSet.selectAncestors(p, false, Expression.IGNORE_CONTEXT);
break;
case Constants.ANCESTOR_SELF_AXIS:
temp = contextSet.selectAncestors(p, true, Expression.IGNORE_CONTEXT);
break;
case Constants.PARENT_AXIS:
// TODO : understand why the contextSet is not involved
// here
// NodeProxy.getParent returns a *theoretical* parent
// which is *not* guaranteed to be in the context set !
temp = p.getParents(Expression.NO_CONTEXT_ID);
break;
case Constants.PRECEDING_AXIS:
temp = contextSet.selectPreceding(p, Expression.IGNORE_CONTEXT);
break;
case Constants.PRECEDING_SIBLING_AXIS:
temp = contextSet.selectPrecedingSiblings(p, Expression.IGNORE_CONTEXT);
break;
case Constants.FOLLOWING_SIBLING_AXIS:
temp = contextSet.selectFollowingSiblings(p, Expression.IGNORE_CONTEXT);
reverseAxis = false;
break;
case Constants.FOLLOWING_AXIS:
temp = contextSet.selectFollowing(p, Expression.IGNORE_CONTEXT);
reverseAxis = false;
break;
case Constants.SELF_AXIS:
temp = p;
reverseAxis = false;
break;
default:
throw new IllegalArgumentException("Tried to test unknown axis");
}
if (!temp.isEmpty()) {
for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
final NumericValue v = (NumericValue) j.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
final int pos = (reverseAxis ? temp.getItemCount() - v.getInt() : v.getInt() - 1);
// Other positions are ignored
if (pos >= 0 && pos < temp.getItemCount()) {
final NodeProxy t = (NodeProxy) temp.itemAt(pos);
// for the current context: filter out those
// context items not selected by the positional predicate
ContextItem ctx = t.getContext();
t.clearContext(Expression.IGNORE_CONTEXT);
while (ctx != null) {
if (ctx.getContextId() == outerContextId) {
if (ctx.getNode().getNodeId().equals(p.getNodeId())) {
t.addContextNode(outerContextId, ctx.getNode());
}
} else {
t.addContextNode(ctx.getContextId(), ctx.getNode());
}
ctx = ctx.getNextDirect();
}
result.add(t);
}
}
}
}
}
}
return result;
} else {
final boolean reverseAxis = Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && (mode == Constants.ANCESTOR_AXIS || mode == Constants.ANCESTOR_SELF_AXIS || mode == Constants.PARENT_AXIS || mode == Constants.PRECEDING_AXIS || mode == Constants.PRECEDING_SIBLING_AXIS);
final Set<NumericValue> set = new TreeSet<>();
final ValueSequence result = new ValueSequence();
for (final SequenceIterator i = innerSeq.iterate(); i.hasNext(); ) {
final NumericValue v = (NumericValue) i.nextItem();
// Non integers return... nothing, not even an error !
if (!v.hasFractionalPart() && !v.isZero()) {
final int pos = (reverseAxis ? contextSequence.getItemCount() - v.getInt() : v.getInt() - 1);
// Other positions are ignored
if (pos >= 0 && pos < contextSequence.getItemCount() && !set.contains(v)) {
result.add(contextSequence.itemAt(pos));
set.add(v);
}
}
}
return result;
}
}
use of org.exist.dom.persistent.NodeProxy in project exist by eXist-db.
the class ValueComparison method nodeSetCompare.
protected Sequence nodeSetCompare(NodeSet nodes, Sequence contextSequence) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "nodeSetCompare");
}
final NodeSet result = new ExtArrayNodeSet();
final Collator collator = getCollator(contextSequence);
if (contextSequence != null && !contextSequence.isEmpty()) {
for (final NodeProxy current : nodes) {
ContextItem context = current.getContext();
if (context == null) {
throw new XPathException(this, ErrorCodes.XPDY0002, "Context is missing for node set comparison");
}
do {
final AtomicValue lv = current.atomize();
final Sequence rs = getRight().eval(context.getNode().toSequence());
if (!rs.hasOne()) {
throw new XPathException(this, ErrorCodes.XPTY0004, "Type error: sequence with less or more than one item is not allowed here");
}
if (compareAtomic(collator, lv, rs.itemAt(0).atomize(), StringTruncationOperator.NONE, relation)) {
result.add(current);
}
} while ((context = context.getNextDirect()) != null);
}
} else {
final Sequence rs = getRight().eval(null);
if (!rs.hasOne()) {
throw new XPathException(this, ErrorCodes.XPTY0004, "Type error: sequence with less or more than one item is not allowed here");
}
final AtomicValue rv = rs.itemAt(0).atomize();
for (final NodeProxy current : nodes) {
final AtomicValue lv = current.atomize();
if (compareAtomic(collator, lv, rv, StringTruncationOperator.NONE, Comparison.EQ)) {
result.add(current);
}
}
}
return result;
}
Aggregations