/** * This file is part of d:swarm graph extension. * * d:swarm graph extension is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * d:swarm graph extension is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with d:swarm graph extension. If not, see <http://www.gnu.org/licenses/>. */ package org.dswarm.graph.rdf.read; import java.util.HashMap; import java.util.Map; import com.google.common.base.Optional; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.DynamicLabel; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.ResourceIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.dswarm.graph.DMPGraphException; import org.dswarm.graph.index.NamespaceIndex; import org.dswarm.graph.model.GraphStatics; import org.dswarm.graph.read.NodeHandler; import org.dswarm.graph.read.RelationshipHandler; import org.dswarm.graph.tx.TransactionHandler; /** * @author tgaengler */ public class PropertyGraphRDFReader implements RDFReader { private static final Logger LOG = LoggerFactory.getLogger(PropertyGraphRDFReader.class); private final NodeHandler nodeHandler; private final NodeHandler startNodeHandler; private final RelationshipHandler relationshipHandler; private final String prefixedRecordClassUri; private final String prefixedDataModelUri; private final GraphDatabaseService database; private final TransactionHandler tx; private final NamespaceIndex namespaceIndex; private Model model; public PropertyGraphRDFReader(final String prefixedRcordClassUriArg, final String prefixedDataModelUriArg, final GraphDatabaseService databaseArg, final TransactionHandler txArg, final NamespaceIndex namespaceIndexArg) throws DMPGraphException { prefixedRecordClassUri = prefixedRcordClassUriArg; database = databaseArg; tx = txArg; namespaceIndex = namespaceIndexArg; nodeHandler = new CBDNodeHandler(); startNodeHandler = new CBDStartNodeHandler(); relationshipHandler = new CBDRelationshipHandler(); prefixedDataModelUri = prefixedDataModelUriArg; } @Override public Optional<Model> read() throws DMPGraphException { tx.ensureRunningTx(); try { LOG.debug("start read RDF TX"); final Label recordClassLabel = DynamicLabel.label(prefixedRecordClassUri); final ResourceIterator<Node> recordNodes = database.findNodes( recordClassLabel, GraphStatics.DATA_MODEL_PROPERTY, prefixedDataModelUri); if (recordNodes == null) { tx.succeedTx(); return Optional.absent(); } model = ModelFactory.createDefaultModel(); while (recordNodes.hasNext()) { final Node recordNode = recordNodes.next(); startNodeHandler.handleNode(recordNode); } recordNodes.close(); tx.succeedTx(); } catch (final Exception e) { final String message = "couldn't finish read RDF TX successfully"; tx.failTx(); LOG.error(message, e); throw new DMPGraphException(message); } return Optional.of(model); } @Override public long countStatements() { return model.size(); } private class CBDNodeHandler implements NodeHandler { @Override public void handleNode(final Node node) throws DMPGraphException { // TODO: find a better way to determine the end of a resource description, e.g., add a property "resource" to each // node that holds the uri of the resource (record) // => maybe we should find an appropriated cypher query as replacement for this processing if (!node.hasProperty(GraphStatics.URI_PROPERTY)) { final Iterable<Relationship> relationships = node.getRelationships(Direction.OUTGOING); for (final Relationship relationship : relationships) { relationshipHandler.handleRelationship(relationship); } } } } private class CBDStartNodeHandler implements NodeHandler { @Override public void handleNode(final Node node) throws DMPGraphException { // TODO: find a better way to determine the end of a resource description, e.g., add a property "resource" to each // node that holds the uri of the resource (record) if (node.hasProperty(GraphStatics.URI_PROPERTY)) { final Iterable<Relationship> relationships = node.getRelationships(Direction.OUTGOING); for (final Relationship relationship : relationships) { relationshipHandler.handleRelationship(relationship); } } } } private class CBDRelationshipHandler implements RelationshipHandler { final Map<Long, Resource> bnodes = new HashMap<>(); final Map<String, Resource> resources = new HashMap<>(); @Override public void handleRelationship(final Relationship rel) throws DMPGraphException { if (rel.getProperty(GraphStatics.DATA_MODEL_PROPERTY).equals(prefixedDataModelUri)) { // TODO: utilise node type for switch final String prefixedSubjectURI = (String) rel.getStartNode().getProperty(GraphStatics.URI_PROPERTY, null); final Resource subjectResource; if (prefixedSubjectURI == null) { // prefixedSubjectURI is a bnode final long subjectId = rel.getStartNode().getId(); subjectResource = createResourceFromBNode(subjectId); } else { final String fullObjectUri = namespaceIndex.createFullURI(prefixedSubjectURI); subjectResource = createResourceFromURI(fullObjectUri); } final String prefixedPredicateURI = rel.getType().name(); final String predicateURI = namespaceIndex.createFullURI(prefixedPredicateURI); //.getProperty(GraphStatics.URI_PROPERTY, null); final Property predicate = model.createProperty(predicateURI); final String object; final String prefixedObjectURI = (String) rel.getEndNode().getProperty(GraphStatics.URI_PROPERTY, null); final Resource objectResource; // TODO: utilise node type for switch if (prefixedObjectURI != null) { // object is a resource final String fullResourceUri = namespaceIndex.createFullURI(prefixedObjectURI); objectResource = createResourceFromURI(fullResourceUri); } else { // check, whether object is a bnode if (!rel.getEndNode().hasProperty(GraphStatics.VALUE_PROPERTY)) { // object is a bnode final long objectId = rel.getEndNode().getId(); objectResource = createResourceFromBNode(objectId); } else { // object is a literal node object = (String) rel.getEndNode().getProperty(GraphStatics.VALUE_PROPERTY, null); model.add(subjectResource, predicate, object); return; } } model.add(subjectResource, predicate, objectResource); // continue traversal with object node nodeHandler.handleNode(rel.getEndNode()); } } private Resource createResourceFromBNode(final long bnodeId) { if (!bnodes.containsKey(bnodeId)) { bnodes.put(bnodeId, model.createResource()); } return bnodes.get(bnodeId); } private Resource createResourceFromURI(final String uri) { if (!resources.containsKey(uri)) { // TODO: name space prefixing resources.put(uri, model.createResource(uri)); } return resources.get(uri); } } }