Skip to content

Commit

Permalink
Improved JavaDocs
Browse files Browse the repository at this point in the history
  • Loading branch information
ajs6f committed Mar 16, 2015
1 parent ab2c2dc commit 9d8fc7f
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 30 deletions.
Expand Up @@ -36,6 +36,10 @@
import com.hp.hpl.jena.rdf.model.Statement;

/**
* Deskolemization is abstractly a function from RDF nodes to RDF nodes, but here we implement it, purely for
* convenience of operation, as a function from triples to triples. This object should be used to translate only one
* document's scope of RDF.
*
* @author ajs6f
*/
public class Deskolemizer implements Function<Triple, Triple> {
Expand All @@ -44,7 +48,17 @@ public class Deskolemizer implements Function<Triple, Triple> {

private final Model context;

private LoadingCache<Resource, Resource> bnodeSubstitutions;
/**
* A map from which to select blank nodes as replacements for Skolem nodes.
*/
private final LoadingCache<Resource, Resource> bnodeSubstitutions = newBuilder().build(from(
new Supplier<Resource>() {

@Override
public Resource get() {
return context.createResource();
}
}));

private static final Logger log = getLogger(Deskolemizer.class);

Expand All @@ -55,25 +69,18 @@ public class Deskolemizer implements Function<Triple, Triple> {
public Deskolemizer(final IdentifierConverter<Resource, FedoraResource> idTranslator, final Model model) {
this.idTranslator = idTranslator;
this.context = model == null ? createDefaultModel() : model;
this.bnodeSubstitutions = newBuilder().build(from(new Supplier<Resource>() {

@Override
public Resource get() {
return context.createResource();
}
}));
}

@Override
public Triple apply(final Triple t) {
log.debug("Deskolemizing: {}", t);
final Statement stmnt = context.asStatement(t);

final Resource s = stmnt.getSubject();
final RDFNode o = stmnt.getObject();
try {
final Resource subject = deskolemize(s).asResource();
final RDFNode object = deskolemize(o);
// predicates cannot be blank nodes in RDF 1.1
final Triple deskolemized = context.createStatement(subject, stmnt.getPredicate(), object).asTriple();
log.debug("Deskolemized to {}", deskolemized);
return deskolemized;
Expand All @@ -83,6 +90,12 @@ public Triple apply(final Triple t) {
}
}

/**
* The node-to-node deskolemization function.
*
* @param n
* @return a deskolemized version of n
*/
private RDFNode deskolemize(final RDFNode n) {
log.debug("Deskolemizing RDF node: {}", n);
if (isSkolem(n)) {
Expand Down
Expand Up @@ -67,9 +67,11 @@ public Statement apply(final Statement stmnt) {
return stmnt;
}

/**
* @return Any nodes for hash URI resources that have been generated and might need to be persisted.
*/
@Override
public Set<Resource> get() {
return hashNodes;
}

}
Expand Up @@ -16,9 +16,9 @@

package org.fcrepo.kernel.impl.rdf;

import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel;
import static com.google.common.hash.Hashing.murmur3_32;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource;
import static com.hp.hpl.jena.vocabulary.RDF.type;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createStatement;
import static java.util.UUID.randomUUID;
import static org.fcrepo.kernel.RdfLexicon.REPOSITORY_NAMESPACE;
import static org.slf4j.LoggerFactory.getLogger;
Expand All @@ -30,26 +30,30 @@

import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.hash.HashCode;
import com.hp.hpl.jena.rdf.model.AnonId;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.RDFVisitor;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;

/**
* Skolemizes blank nodes in a statement.
* Skolemization is abstractly a function from RDF nodes to RDF nodes, but here we implement it, purely for
* convenience of operation, as a function from triples to triples. {@link #nodeSkolemizer} represents the
* node-to-node function. This class will make two blank nodes with the same identifier into the same skolem resource
* because it uses only one prefix for its lifetime and because the mapping from a given blank node to a skolem
* resource depends only on that prefix and on a MurmurHash3 of the identifier of the blank node in question. An
* instance of this class should be used only with a contextual topic of one single resource and only for one
* document's scope of RDF about that resource.
*
* @author ajs6f
*/
public class Skolemizer implements Function<Statement, Statement>, Supplier<Set<Resource>> {

private final RDFVisitor nodeSkolemizer;

private final Model model;

public static final Resource SKOLEM_TYPE = createResource(REPOSITORY_NAMESPACE + "skolem");

private final Set<Resource> skolemNodes = new HashSet<>();
Expand All @@ -61,10 +65,9 @@ public class Skolemizer implements Function<Statement, Statement>, Supplier<Set<
* be skolemized to identifiers rooted at this URI.
*/
public Skolemizer(final Resource topic) {
this.model = topic.getModel() == null ? createDefaultModel() : topic.getModel();
// TODO use Java 8's StringJoiner facility.
final String prefix = topic + "/" + randomUUID().toString().replace('-', '/');
this.nodeSkolemizer = new NodeSkolemizer(prefix, model);
this.nodeSkolemizer = new NodeSkolemizer(prefix);
}

@Override
Expand All @@ -80,43 +83,38 @@ public Statement apply(final Statement stmnt) {
if (o.isResource() && !o.equals(object)) {
skolemNodes.add(object.asResource());
}
// predicates are never anonymous
// predicates are never anonymous in RDF 1.1
final Property predicate = stmnt.getPredicate();
final Statement skolemized = model.createStatement(subject, predicate, object);
final Statement skolemized = createStatement(subject, predicate, object);
log.debug("to: {}", skolemized);
return skolemized;
}

/**
* @return Any Skolem nodes that might need to be created.
* @return Any Skolem nodes that have been generated and might need to be persisted.
*/
@Override
public Set<Resource> get() {
return skolemNodes;
}

/**
* Does nothing to literals or URIs, skolemizes bnodes.
* Does nothing to literals or URIs, but skolemizes blank nodes.
*
* @author ajs6f
*/
private static class NodeSkolemizer implements RDFVisitor {

private final String prefix;

private final Model model;

public NodeSkolemizer(final String prefix, final Model model) {
public NodeSkolemizer(final String prefix) {
this.prefix = prefix;
this.model = model;
}

@Override
public Resource visitBlank(final Resource r, final AnonId id) {
final Resource skolemNode = model.createResource(prefix + "/" + id.getLabelString().replace(':', '-'));
// ensures that this skolem node is recognizable as such.
model.add(model.createStatement(skolemNode, type, SKOLEM_TYPE));
return skolemNode;
final HashCode suffix = murmur3_32().hashBytes(id.getLabelString().getBytes());
return createResource(prefix + "/" + suffix);
}

@Override
Expand Down

0 comments on commit 9d8fc7f

Please sign in to comment.