![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.clerezza.utils.GraphNode Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you 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.clerezza.utils;
import org.apache.clerezza.*;
import org.apache.clerezza.implementation.TripleImpl;
import org.apache.clerezza.implementation.in_memory.SimpleGraph;
import org.apache.clerezza.implementation.literal.LiteralFactory;
import java.util.*;
import java.util.concurrent.locks.Lock;
/**
* This class represents a node in the context of a graph. It provides
* utility methods to explore and modify its neighbourhood. The method
* modifying the graph will throw an {@link UnsupportedOperationException}
* it the underlying Graph in immutable (i.e. is a {@link ImmutableGraph}.
*
* @author reto, mir
* @since 0.2
*/
public class GraphNode {
private final RDFTerm resource;
private final Graph graph;
/**
* Create a GraphNode representing resource within graph.
*
* @param resource the resource this GraphNode represents
* @param graph the Graph that describes the resource
*/
public GraphNode(RDFTerm resource, Graph graph) {
if (resource == null) {
throw new IllegalArgumentException("resource may not be null");
}
if (graph == null) {
throw new IllegalArgumentException("graph may not be null");
}
this.resource = resource;
this.graph = graph;
}
/**
* Gets the graph the node represented by this instance is in
*
* @return the graph of this GraphNode
*/
public Graph getGraph() {
return graph;
}
/**
* Gets the unwrapped node
*
* @return the node represented by this GraphNode
*/
public RDFTerm getNode() {
return resource;
}
/**
* Deletes the context of a node
*
* @see getNodeContext()
*/
public void deleteNodeContext() {
for (Triple triple : getNodeContext()) {
graph.remove(triple);
}
}
/**
* The context of a node are the triples containing a node
* as subject or object and recursively the context of the b-nodes in any
* of these statements.
*
* The triples in the ImmutableGraph returned by this method contain the same bnode
* instances as in the original graph.
*
* @return the context of the node represented by the instance
*/
public ImmutableGraph getNodeContext() {
Lock l = readLock();
l.lock();
try {
final HashSet dontExpand = new HashSet();
dontExpand.add(resource);
if (resource instanceof IRI) {
return getContextOf((IRI) resource, dontExpand).getImmutableGraph();
}
return getContextOf(resource, dontExpand).getImmutableGraph();
} finally {
l.unlock();
}
}
private Graph getContextOf(IRI node, final Set dontExpand) {
final String uriPrefix = node.getUnicodeString() + '#';
return getContextOf(node, dontExpand, new Acceptor() {
@Override
public boolean expand(RDFTerm resource) {
if (resource instanceof BlankNode) {
return true;
}
if (resource instanceof IRI) {
return ((IRI) resource).getUnicodeString().startsWith(uriPrefix);
}
return false;
}
});
}
/**
* Returns the context of a BlankNodeOrIRI
*
* @param node
* @param dontExpand a list of bnodes at which to stop expansion, if node
* is a BlankNode it should be contained (potentially faster)
* @return the context of a node
*/
private Graph getContextOf(RDFTerm node, final Set dontExpand) {
return getContextOf(node, dontExpand, new Acceptor() {
@Override
public boolean expand(RDFTerm resource) {
if (resource instanceof BlankNode) {
return true;
}
return false;
}
});
}
private interface Acceptor {
boolean expand(RDFTerm resource);
}
private Graph getContextOf(RDFTerm node, final Set dontExpand, Acceptor acceptor) {
Graph result = new SimpleGraph();
if (node instanceof BlankNodeOrIRI) {
Iterator forwardProperties = graph.filter((BlankNodeOrIRI) node, null, null);
while (forwardProperties.hasNext()) {
Triple triple = forwardProperties.next();
result.add(triple);
RDFTerm object = triple.getObject();
if (acceptor.expand(object) && !dontExpand.contains(object)) {
dontExpand.add(object);
result.addAll(getContextOf(object, dontExpand, acceptor));
}
}
}
Iterator backwardProperties = graph.filter(null, null, node);
while (backwardProperties.hasNext()) {
Triple triple = backwardProperties.next();
result.add(triple);
BlankNodeOrIRI subject = triple.getSubject();
if (acceptor.expand(subject) && !dontExpand.contains(subject)) {
dontExpand.add(subject);
result.addAll(getContextOf(subject, dontExpand, acceptor));
}
}
return result;
}
private Iterator getTypeSelectedObjects(IRI property, final Class type) {
final Iterator objects = getObjects(property);
return new Iterator() {
T next = prepareNext();
@Override
public boolean hasNext() {
return next != null;
}
@Override
public T next() {
T result = next;
next = prepareNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
private T prepareNext() {
while (objects.hasNext()) {
RDFTerm nextObject = objects.next();
if (type.isAssignableFrom(nextObject.getClass())) {
return (T) nextObject;
}
}
return null;
}
};
}
public Iterator getLiterals(IRI property) {
final Iterator objects = getObjects(property);
return new Iterator() {
Literal next = prepareNext();
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Literal next() {
Literal result = next;
next = prepareNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
private Literal prepareNext() {
while (objects.hasNext()) {
RDFTerm nextObject = objects.next();
if (nextObject instanceof Literal) {
return (Literal) nextObject;
}
}
return null;
}
};
}
/**
* Count the number of triples in the underlying triple-collection
* with this node as subject and a specified property as predicate.
*
* @param property the property to be examined
* @return the number of triples in the underlying triple-collection
* which meet the specified condition
*/
public int countObjects(IRI property) {
return countTriples(graph.filter((BlankNodeOrIRI) resource, property, null));
}
private int countTriples(final Iterator triples) {
int count = 0;
while (triples.hasNext()) {
triples.next();
count++;
}
return count;
}
/**
* Get the objects of statements with this node as subject and a specified
* property as predicate.
*
* @param property the property
* @return
*/
public Iterator getObjects(IRI property) {
if (resource instanceof BlankNodeOrIRI) {
final Iterator triples = graph.filter((BlankNodeOrIRI) resource, property, null);
return new Iterator() {
@Override
public boolean hasNext() {
return triples.hasNext();
}
@Override
public RDFTerm next() {
final Triple triple = triples.next();
if (triple != null) {
return triple.getObject();
} else {
return null;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
} else {
return new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public RDFTerm next() {
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
}
/**
* Checks wether this node has the given property with the given value.
* If the given value is null, then it is checked if this node has the
* specified property regardless of its value.
*
* @param property
* @param object
* @return true if the node represented by this object is the subject of a
* statement with the given prediate and object, false otherwise
*/
public boolean hasProperty(IRI property, RDFTerm object) {
Lock l = readLock();
l.lock();
try {
Iterator objects = getObjects(property);
if (object == null) {
return objects.hasNext();
}
while (objects.hasNext()) {
if (objects.next().equals(object)) {
return true;
}
}
return false;
} finally {
l.unlock();
}
}
/**
* Count the number of triples in the underlying triple-collection
* with this node as object and a specified property as predicate.
*
* @param property the property to be examined
* @return the number of triples in the underlying triple-collection
* which meet the specified condition
*/
public int countSubjects(IRI property) {
Lock l = readLock();
l.lock();
try {
return countTriples(graph.filter(null, property, resource));
} finally {
l.unlock();
}
}
/**
* Get the subjects of statements with this node as object and a specified
* property as predicate.
*
* @param property the property
* @return
*/
public Iterator getSubjects(IRI property) {
final Iterator triples = graph.filter(null, property, resource);
return new Iterator() {
@Override
public boolean hasNext() {
return triples.hasNext();
}
@Override
public BlankNodeOrIRI next() {
return triples.next().getSubject();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
public Iterator getIRIObjects(IRI property) {
return getTypeSelectedObjects(property, IRI.class);
}
/**
* Get all available properties as an {@link Iterator}<{@link IRI}>.
* You can use getObjects(IRI property)
to get the values of
* each property
*
* @return an iterator over properties of this node
*/
public Iterator getProperties() {
if (resource instanceof BlankNodeOrIRI) {
final Iterator triples = graph.filter((BlankNodeOrIRI) resource, null, null);
return getUniquePredicates(triples);
} else {
return new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public IRI next() {
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
}
/**
* Get all inverse properties as an {@link Iterator}<{@link IRI}>.
* You can use getSubject(IRI property)
to get the values of
* each inverse property
*
* @return an iterator over properties pointing to this node
*/
public Iterator getInverseProperties() {
final Iterator triples = graph.filter(null, null, resource);
return getUniquePredicates(triples);
}
/**
* @param triples
* @returnan {@link Iterator}<{@link IRI}> containing the predicates from
* an {@link Iterator}<{@link Triple}>
*/
private Iterator getUniquePredicates(final Iterator triples) {
final Set resultSet = new HashSet();
while (triples.hasNext()) {
resultSet.add(triples.next().getPredicate());
}
return resultSet.iterator();
}
/**
* Adds a property to the node with the specified predicate and object
*
* @param predicate
* @param object
*/
public void addProperty(IRI predicate, RDFTerm object) {
if (resource instanceof BlankNodeOrIRI) {
graph.add(new TripleImpl((BlankNodeOrIRI) resource, predicate, object));
} else {
throw new RuntimeException("Literals cannot be the subject of a statement");
}
}
/**
* Coverts the value into a typed literals and sets it as object of the
* specified property
*
* @param property the predicate of the triple to be created
* @param value the value of the typed literal object
*/
public void addPropertyValue(IRI property, Object value) {
addProperty(property,
LiteralFactory.getInstance().createTypedLiteral(value));
}
/**
* Adds a property to the node with the inverse of the specified predicate and object
* In other words subject
will be related via the property relation
to this node.
*
* @param predicate
* @param subject
*/
public void addInverseProperty(IRI predicate, RDFTerm subject) {
if (subject instanceof BlankNodeOrIRI) {
graph.add(new TripleImpl((BlankNodeOrIRI) subject, predicate, resource));
} else {
throw new RuntimeException("Literals cannot be the subject of a statement");
}
}
/**
* creates and returns an RdfList
for the node and
* Graph represented by this object.
*
* @return a List to easy access the rdf:List represented by this node
*/
public List asList() {
if (resource instanceof BlankNodeOrIRI) {
return new RdfList((BlankNodeOrIRI) resource, graph);
} else {
throw new RuntimeException("Literals cannot be the subject of a List");
}
}
/**
* Deletes all statement with the current node as subject and the specified
* predicate
*
* @param predicate
*/
public void deleteProperties(IRI predicate) {
if (resource instanceof BlankNodeOrIRI) {
Iterator tripleIter = graph.filter((BlankNodeOrIRI) resource, predicate, null);
Collection toDelete = new ArrayList();
while (tripleIter.hasNext()) {
Triple triple = tripleIter.next();
toDelete.add(triple);
}
for (Triple triple : toDelete) {
graph.remove(triple);
}
}
}
/**
* Delete property to the node with the specified predicate and object
*
* @param predicate
* @param object
*/
public void deleteProperty(IRI predicate, RDFTerm object) {
if (resource instanceof BlankNodeOrIRI) {
graph.remove(new TripleImpl((BlankNodeOrIRI) resource, predicate, object));
}
}
@Override
public String toString() {
return resource.toString();
}
/**
* Replaces the graph node resouce with the specified BlankNodeOrIRI
.
* The resource is only replaced where it is either subject or object.
*
* @param replacement
* @return a GraphNode representing the replecement node
*/
public GraphNode replaceWith(BlankNodeOrIRI replacement) {
return replaceWith(replacement, false);
}
/**
* Replaces the graph node resouce with the specified BlankNodeOrIRI
.
* Over the boolean checkPredicate
it can be specified if the
* resource should also be replaced where it is used as predicate.
*
* @param replacement
* @param checkPredicates
* @return a GraphNode representing the replecement node
*/
public GraphNode replaceWith(BlankNodeOrIRI replacement, boolean checkPredicates) {
Graph newTriples = new SimpleGraph();
if (!(resource instanceof Literal)) {
Iterator subjectTriples = graph.filter((BlankNodeOrIRI) resource, null,
null);
while (subjectTriples.hasNext()) {
Triple triple = subjectTriples.next();
Triple newTriple = new TripleImpl(replacement, triple.getPredicate(),
triple.getObject());
subjectTriples.remove();
newTriples.add(newTriple);
}
graph.addAll(newTriples);
newTriples.clear();
}
Iterator objectTriples = graph.filter(null, null, resource);
while (objectTriples.hasNext()) {
Triple triple = objectTriples.next();
Triple newTriple = new TripleImpl(triple.getSubject(),
triple.getPredicate(), replacement);
objectTriples.remove();
newTriples.add(newTriple);
}
graph.addAll(newTriples);
newTriples.clear();
if (checkPredicates && replacement instanceof IRI
&& resource instanceof IRI) {
Iterator predicateTriples = graph.filter(null,
(IRI) resource, null);
while (predicateTriples.hasNext()) {
Triple triple = predicateTriples.next();
Triple newTriple = new TripleImpl(triple.getSubject(),
(IRI) replacement, triple.getObject());
predicateTriples.remove();
newTriples.add(newTriple);
}
graph.addAll(newTriples);
}
return new GraphNode(replacement, graph);
}
/**
* Returns a iterator containing all objects of the triples where this
* graph node is the subject and has the specified property. The objects
* are returned as GraphNode
s.
*
* @param property
* @return
*/
public Iterator getObjectNodes(IRI property) {
final Iterator objects = this.getObjects(property);
return new Iterator() {
@Override
public boolean hasNext() {
return objects.hasNext();
}
@Override
public GraphNode next() {
RDFTerm object = objects.next();
return new GraphNode(object, graph);
}
@Override
public void remove() {
objects.remove();
}
};
}
/**
* Returns a iterator containing all subjects of the triples where this
* graph node is the object and has the specified property. The subjects
* are returned as GraphNode
s.
*
* @param property
* @return
*/
public Iterator getSubjectNodes(IRI property) {
final Iterator subjects = this.getSubjects(property);
return new Iterator() {
@Override
public boolean hasNext() {
return subjects.hasNext();
}
@Override
public GraphNode next() {
RDFTerm object = subjects.next();
return new GraphNode(object, graph);
}
@Override
public void remove() {
subjects.remove();
}
};
}
/**
* @param obj
* @return true if obj is an instance of the same class represening the same
* node in the same graph, subclasses may have different identity criteria.
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj.getClass().equals(getClass()))) {
return false;
}
GraphNode other = (GraphNode) obj;
return getNode().equals(other.getNode())
&& getGraph().equals(other.getGraph());
}
@Override
public int hashCode() {
return 13 * getNode().hashCode() + getGraph().hashCode();
}
/**
* @return a ReadLock if the underlying ImmutableGraph is a LockableGraph it returns its lock, otherwise null
*/
public Lock readLock() {
return getGraph().getLock().readLock();
}
/**
* @return
*/
public Lock writeLock() {
return (getGraph()).getLock().writeLock();
}
}