/** * PODD is an OWL ontology database used for scientific project management * * Copyright (C) 2009-2013 The University Of Queensland * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see <http://www.gnu.org/licenses/>. */ package com.github.podd.impl.purl; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.query.GraphQuery; import org.openrdf.query.GraphQueryResult; import org.openrdf.query.MalformedQueryException; import org.openrdf.query.QueryEvaluationException; import org.openrdf.query.QueryLanguage; import org.openrdf.query.UpdateExecutionException; import org.openrdf.query.impl.DatasetImpl; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.podd.api.PoddProcessorStage; import com.github.podd.api.purl.PoddPurlManager; import com.github.podd.api.purl.PoddPurlProcessor; import com.github.podd.api.purl.PoddPurlProcessorFactory; import com.github.podd.api.purl.PoddPurlProcessorFactoryRegistry; import com.github.podd.api.purl.PoddPurlReference; import com.github.podd.exception.PurlProcessorNotHandledException; import com.github.podd.utils.PoddRdfProcessorUtils; /** * Basic PURL Manager implementation for use in PODD. * * * @author kutila * */ public class PoddPurlManagerImpl implements PoddPurlManager { protected final Logger log = LoggerFactory.getLogger(this.getClass()); // This manager functions only during the RDF_PARSING processor stage. private final PoddProcessorStage processorStage = PoddProcessorStage.RDF_PARSING; private PoddPurlProcessorFactoryRegistry purlProcessorFactoryRegistry; private void convertTemporaryUri(final URI inputUri, final URI outputUri, final RepositoryConnection repositoryConnection, final URI... contexts) throws RepositoryException { // replace occurrences as Subject final RepositoryResult<Statement> stmtsWithTempSubject = repositoryConnection.getStatements(inputUri, null, null, false, contexts); try { while(stmtsWithTempSubject.hasNext()) { final Statement next = stmtsWithTempSubject.next(); repositoryConnection.add(outputUri, next.getPredicate(), next.getObject(), contexts); repositoryConnection.remove(next, contexts); } } finally { stmtsWithTempSubject.close(); } // replace occurrences as Object final RepositoryResult<Statement> stmtsWithTempObject = repositoryConnection.getStatements(null, null, inputUri, false, contexts); try { while(stmtsWithTempObject.hasNext()) { final Statement next = stmtsWithTempObject.next(); repositoryConnection.add(next.getSubject(), next.getPredicate(), outputUri, contexts); repositoryConnection.remove(next, contexts); } } finally { stmtsWithTempObject.close(); } } @Override public void convertTemporaryUris(final Set<PoddPurlReference> purlResults, final RepositoryConnection repositoryConnection, final URI... contexts) throws RepositoryException, UpdateExecutionException { for(final PoddPurlReference purl : purlResults) { final URI inputUri = purl.getTemporaryURI(); final URI outputUri = purl.getPurlURI(); this.log.debug("Converting: {} to {}", inputUri, outputUri); this.convertTemporaryUri(inputUri, outputUri, repositoryConnection, contexts); } } @Override public Set<PoddPurlReference> extractPurlReferences(final RepositoryConnection repositoryConnection, final URI... contexts) throws PurlProcessorNotHandledException, RepositoryException { return this.extractPurlReferences(null, repositoryConnection, contexts); } @Override public Set<PoddPurlReference> extractPurlReferences(final URI parentUri, final RepositoryConnection repositoryConnection, final URI... contexts) throws PurlProcessorNotHandledException, RepositoryException { final Set<PoddPurlReference> internalPurlResults = Collections.newSetFromMap(new ConcurrentHashMap<PoddPurlReference, Boolean>()); // NOTE: We use a Set to avoid duplicate calls to any Purl processors // for any // temporary URI final Set<URI> temporaryURIs = Collections.newSetFromMap(new ConcurrentHashMap<URI, Boolean>()); // NOTE: a Factory may handle only a particular temporary URI format, // necessitating to // go through multiple factories to extract ALL temporary URIs in the // Repository. for(final PoddPurlProcessorFactory nextProcessorFactory : this.getPurlProcessorFactoryRegistry().getByStage( this.processorStage)) { try { final String sparqlQuery = PoddRdfProcessorUtils.buildSparqlConstructQuery(nextProcessorFactory); final GraphQuery graphQuery = repositoryConnection.prepareGraphQuery(QueryLanguage.SPARQL, sparqlQuery); // Create a new dataset to specify contexts that the query will // be allowed to access final DatasetImpl dataset = new DatasetImpl(); for(final URI artifactGraphUri : contexts) { dataset.addDefaultGraph(artifactGraphUri); dataset.addNamedGraph(artifactGraphUri); } // set the dataset for the query to be our artificially // constructed dataset graphQuery.setDataset(dataset); final GraphQueryResult queryResult = graphQuery.evaluate(); // If the query matched anything, then for each of the temporary // URIs in the // resulting construct statements, we create a Purl reference // and add it to the // results while(queryResult.hasNext()) { final Statement next = queryResult.next(); // This processor factory matches the graph that we wish to // use, so we create a // processor instance now to create the PURL // NOTE: This object cannot be shared as we do not specify // that it needs to be // thread safe final PoddPurlProcessor processor = nextProcessorFactory.getProcessor(); // Subject rewriting if(next.getSubject() instanceof URI && !temporaryURIs.contains(next.getSubject()) && processor.canHandle((URI)next.getSubject())) { temporaryURIs.add((URI)next.getSubject()); internalPurlResults.add(processor.handleTranslation((URI)next.getSubject(), parentUri)); } // Predicate rewriting is not supported. Predicates in OWL // Documents must // be URIs from recognized vocabularies, so cannot be auto // generated PURLs // Object rewriting if(next.getObject() instanceof URI && !temporaryURIs.contains(next.getObject()) && processor.canHandle((URI)next.getObject())) { temporaryURIs.add((URI)next.getObject()); internalPurlResults.add(processor.handleTranslation((URI)next.getObject(), parentUri)); } } } catch(final MalformedQueryException | QueryEvaluationException e) { this.log.error("Unexpected query exception", e); // continue after logging an error, as another ProcessorFactory // may generate a Purl // for this failed temporary URI } } return internalPurlResults; } @Override public PoddPurlProcessorFactoryRegistry getPurlProcessorFactoryRegistry() { return this.purlProcessorFactoryRegistry; } @Override public void setPurlProcessorRegistry(final PoddPurlProcessorFactoryRegistry purlProcessorFactoryRegistry) { this.purlProcessorFactoryRegistry = purlProcessorFactoryRegistry; } }