/** * 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.export; import java.util.HashMap; import java.util.Map; import org.apache.jena.query.Dataset; 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.RDFNode; import org.apache.jena.rdf.model.Resource; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.dswarm.graph.DMPGraphException; import org.dswarm.graph.NodeType; import org.dswarm.graph.index.NamespaceIndex; import org.dswarm.graph.model.GraphStatics; import org.dswarm.common.rdf.utils.RDFUtils; import org.dswarm.graph.read.RelationshipHandler; import org.dswarm.graph.utils.GraphUtils; /** * @author polowins * @author tgaengler */ public abstract class BaseRDFExporter implements RDFExporter { private static final Logger LOG = LoggerFactory.getLogger(BaseRDFExporter.class); protected final RelationshipHandler relationshipHandler; protected final GraphDatabaseService database; private final NamespaceIndex namespaceIndex; protected Dataset dataset; private long processedStatements = 0; protected long successfullyProcessedStatements = 0; protected static final int JENA_MODEL_WARNING_SIZE = 1000000; // TODO: TransactionHandler public BaseRDFExporter(final GraphDatabaseService databaseArg, final NamespaceIndex namespaceIndexArg) { database = databaseArg; namespaceIndex = namespaceIndexArg; relationshipHandler = new CBDRelationshipHandler(); } @Override public long countStatements() { return RDFUtils.determineDatasetSize(dataset); } private class CBDRelationshipHandler implements RelationshipHandler { // TODO: maybe a hash map is not appropriated for bigger exports final Map<Long, Resource> bnodes = new HashMap<>(); final Map<String, Resource> resources = new HashMap<>(); @Override public void handleRelationship(final Relationship rel) throws DMPGraphException { processedStatements++; // data model final String prefixedDataModelURI = (String) rel.getProperty(GraphStatics.DATA_MODEL_PROPERTY, null); if (prefixedDataModelURI == null) { final String message = String.format("data model URI can't be null (relationship id = '%s')", rel.getId()); BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } final String dataModelURI = namespaceIndex.createFullURI(prefixedDataModelURI); final Model model; if (dataset.containsNamedModel(dataModelURI)) { model = dataset.getNamedModel(dataModelURI); } else { model = ModelFactory.createDefaultModel(); dataset.addNamedModel(dataModelURI, model); } if (model == null) { final String message = String.format("RDF model for graph '%s' ('%s') can't be null (relationship id = '%s')", dataModelURI, prefixedDataModelURI, rel.getId()); BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } // subject final Node subjectNode = rel.getStartNode(); final NodeType subjectNodeType = GraphUtils.determineNodeType(subjectNode); final Resource subjectResource; switch (subjectNodeType) { case Resource: case TypeResource: final String prefixedSubjectURI = (String) subjectNode.getProperty(GraphStatics.URI_PROPERTY, null); if (prefixedSubjectURI == null) { final String message = "subject URI can't be null"; BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } final String fullSubjectURI = namespaceIndex.createFullURI(prefixedSubjectURI); subjectResource = createResourceFromURI(fullSubjectURI, model); break; case BNode: case TypeBNode: final long subjectId = subjectNode.getId(); subjectResource = createResourceFromBNode(subjectId, model); break; default: final String message = "subject node type can only be a resource (or type resource) or bnode (or type bnode)"; BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } // predicateURI final String prefixedPredicate = rel.getType().name(); final String predicateURI = namespaceIndex.createFullURI(prefixedPredicate); //.getProperty(GraphStatics.URI_PROPERTY, null); final Property predicate = model.createProperty(predicateURI); // object final Node objectNode = rel.getEndNode(); final NodeType objectNodeType = GraphUtils.determineNodeType(objectNode); final RDFNode objectRDFNode; switch (objectNodeType) { case Resource: case TypeResource: final String prefixedObjectURI = (String) objectNode.getProperty(GraphStatics.URI_PROPERTY, null); if (prefixedObjectURI == null) { final String message = "object URI can't be null"; BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } final String fullObjectURI = namespaceIndex.createFullURI(prefixedObjectURI); objectRDFNode = createResourceFromURI(fullObjectURI, model); break; case BNode: case TypeBNode: final long objectId = objectNode.getId(); objectRDFNode = createResourceFromBNode(objectId, model); break; case Literal: final String object = (String) objectNode.getProperty(GraphStatics.VALUE_PROPERTY, null); if (object == null) { final String message = "object value can't be null"; BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } if (objectNode.hasProperty(GraphStatics.DATATYPE_PROPERTY)) { final String literalType = (String) objectNode.getProperty(GraphStatics.DATATYPE_PROPERTY, null); if (literalType != null) { // object is a typed literal node objectRDFNode = model.createTypedLiteral(object, literalType); break; } } // object is an untyped literal node objectRDFNode = model.createLiteral(object); break; default: final String message = String.format("unknown node type %s for object node", objectNodeType.getName()); BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } if (subjectResource == null || predicate == null || objectRDFNode == null) { final String message = String.format("couldn't determine the complete statement (subject-predicateURI-object + data model) for relationship '%s'", rel.getId()); BaseRDFExporter.LOG.error(message); throw new DMPGraphException(message); } model.add(subjectResource, predicate, objectRDFNode); successfullyProcessedStatements++; } private Resource createResourceFromBNode(final long bnodeId, final Model model) { if (!bnodes.containsKey(bnodeId)) { bnodes.put(bnodeId, model.createResource()); } return bnodes.get(bnodeId); } private Resource createResourceFromURI(final String uri, final Model model) { if (!resources.containsKey(uri)) { resources.put(uri, model.createResource(uri)); } return resources.get(uri); } } @Override public long processedStatements() { return processedStatements; } @Override public long successfullyProcessedStatements() { return successfullyProcessedStatements; } }