/* * Copyright 2008 Fedora Commons, Inc. * * Licensed 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.mulgara.resolver.prefix; // Java 2 standard packages import java.net.*; import javax.transaction.xa.XAResource; // Third party packages import org.apache.log4j.Logger; import org.jrdf.graph.*; // Locally written packages import org.mulgara.query.*; import org.mulgara.resolver.spi.*; import org.mulgara.store.stringpool.SPObject; import org.mulgara.store.stringpool.SPObjectFactory; import org.mulgara.store.stringpool.StringPoolException; import org.mulgara.store.stringpool.xa.SPObjectFactoryImpl; import org.mulgara.store.tuples.Tuples; import org.mulgara.store.tuples.TuplesOperations; /** * Resolves constraints accessible through a session. * * @created 2005-4-19 * @author <a href="mailto:pgearon@users.sourceforge.net">Paula Gearon</a> * @version $Revision: $ * @modified $Date: $ @maintenanceAuthor $Author: $ * @copyright © 2005 <a href="http://www.fedora-commons.org/">Fedora Commons</a> */ public class PrefixResolver implements Resolver { /** Logger. */ private static final Logger logger = Logger.getLogger(PrefixResolver.class.getName()); /** The session that this resolver is associated with. */ private final ResolverSession resolverSession; /** The URI of the type describing node type models. */ private URI modelTypeURI; /** The preallocated local node representing the mulgara:prefix property. */ private final long mulgaraPrefix; /** The preallocated local node representing the mulgara:stringPrefix property. */ private final long mulgaraStringPrefix; // // Constructors // /** * Construct a resolver. * * @param resolverSession the session this resolver is associated with * @throws IllegalArgumentException if <var>resolverSession</var> is * <code>null</code> * @throws ResolverFactoryException if the superclass is unable to handle its arguments */ PrefixResolver( ResolverSession resolverSession, Resolver systemResolver, long mulgaraPrefix, long mulgaraStringPrefix, URI modelTypeURI ) throws ResolverFactoryException { if (logger.isDebugEnabled()) { logger.debug("Instantiating a node type resolver"); } // Validate "resolverSession" parameter if (resolverSession == null) { throw new IllegalArgumentException( "Null \"resolverSession\" parameter"); } // Initialize fields this.resolverSession = resolverSession; this.modelTypeURI = modelTypeURI; this.mulgaraPrefix = mulgaraPrefix; this.mulgaraStringPrefix = mulgaraStringPrefix; } // // Methods implementing Resolver // /** * Create a model for node types. * * @param model {@inheritDoc}. * @param modelTypeUri {@inheritDoc}. This must be the URI for Prefix models. */ public void createModel(long model, URI modelTypeUri) throws ResolverException { if (logger.isDebugEnabled()) { logger.debug("Create type model " + model); } if (!this.modelTypeURI.equals(modelTypeUri)) { throw new ResolverException("Wrong model type provided as a Prefix model"); } } /** * Expose a callback object for enlistment by a transaction manager. * Uses a dumy xa resource for the moment, but may need to create a fully * functional xa resource which is mapped to a session. * * @return an {@link XAResource} that can be used by a transaction manager to * coordinate this resolver's participation in a distributed transaction. * For now this is a {@link DummyXAResource} with a 10 second transaction timeout * @see javax.resource.spi.ManagedConnection#getXAResource */ public XAResource getXAResource() { return new DummyXAResource( 10 // seconds before transaction timeout ); } /** * Insert or delete RDF statements in an existing model. * This is illegal for this model type. * * @param model the local node identifying an existing model * @param statements the {@link Statements} to insert into the * <var>model</var> * @param occurs whether to assert the <var>statements</var>, or (if * <code>false</code>) to deny it * @throws ResolverException if the <var>statements</var> can't be * added to the <var>model</var> */ public void modifyModel(long model, Statements statements, boolean occurs) throws ResolverException { if (logger.isDebugEnabled()) { logger.debug("Modify prefix model " + model); } throw new ResolverException("Prefix models are read only"); } /** * Remove the cached model containing the contents of a URL. */ public void removeModel(long model) throws ResolverException { if (logger.isDebugEnabled()) { logger.debug("Remove prefix model " + model); } } /** * Resolve a constraint against an RDF/XML document. * * Resolution is by filtration of a URL stream, and thus very slow. */ public Resolution resolve(Constraint constraint) throws QueryException { if (logger.isDebugEnabled()) { logger.debug("Resolve " + constraint); } // Validate "constraint" parameter if (constraint == null) { throw new IllegalArgumentException("Null \"constraint\" parameter"); } if (!(constraint.getModel() instanceof LocalNode)) { if (logger.isDebugEnabled()) logger.debug("Ignoring solutions for " + constraint); return new EmptyResolution(constraint, false); } if ( !(constraint.getElement(1) instanceof LocalNode) || !(constraint.getElement(2) instanceof LocalNode) ) { throw new QueryException("Prefix resolver can only be used for fixed prefixes: " + constraint); } try { long property = ((LocalNode)constraint.getElement(1)).getValue(); LocalNode object = (LocalNode)constraint.getElement(2); Node prefixNode = resolverSession.globalize(object.getValue()); // check the constraint for consistency if ((property != mulgaraPrefix && property != mulgaraStringPrefix) || !(prefixNode instanceof Literal || prefixNode instanceof URIReference)) { if (logger.isDebugEnabled()) { logger.debug("property = " + property +", mulgaraPrefix = " + mulgaraPrefix); logger.debug("element(2): " + prefixNode + " [" + prefixNode.getClass().getName() + "]"); } throw new QueryException("Prefix resolver can only be used for prefix constraints: " + constraint); } if (logger.isDebugEnabled()) { logger.debug("Evaluating " + constraint.getElement(0) + " has prefix " + constraint.getElement(2)); } ConstraintElement node = constraint.getElement(0); assert node != null; Tuples tuples; if (node instanceof Variable) { // convert the prefix into a string pool object SPObjectFactory spoFact = SPObjectFactoryImpl.getInstance(); SPObject startPrefixObj = getStartObject(spoFact, prefixNode, property); SPObject endPrefixObj = getEndObject(spoFact, prefixNode, property); // get the extents of the prefix from the string pool tuples = resolverSession.findStringPoolRange(startPrefixObj, true, endPrefixObj, false); assert tuples != null; // rename variables away from subject, predicate and object tuples.renameVariables(constraint); long resultSize; try { // Get the size of the final result. resultSize = tuples.getRowCount(); } catch (TuplesException e) { throw new QueryException("Unable to build result", e); } if (logger.isDebugEnabled()) { try { logger.debug("tuples size = " + tuples.getRowCount() + " (should be " + resultSize + ")"); } catch (TuplesException e) { logger.debug("Error getting the length of the tuples object"); } } return new TuplesWrapperResolution(tuples, constraint); } else { // if (node instanceof Variable) // node must therefore be an instanceof LocalNode // we can shortcut the process here assert node instanceof LocalNode; LocalNode n = (LocalNode)node; // get the node out of the string pool SPObject spo = resolverSession.findStringPoolObject(n.getValue()); // check that the node exists if (spo == null) { tuples = TuplesOperations.empty(); } else { // see if the node starts with the required prefix String prefix; // extract the string from the literal if (prefixNode instanceof Literal) { prefix = ((Literal) prefixNode).getLexicalForm(); } else { prefix = ((URIReference) prefixNode).getURI().toString(); } if (spo.getLexicalForm().startsWith(prefix)) { tuples = TuplesOperations.unconstrained(); } else { tuples = TuplesOperations.empty(); } } } // convert the tuples to a resolution return new TuplesWrapperResolution(tuples, constraint); } catch (GlobalizeException ge) { throw new QueryException("Couldn't convert internal data into a string", ge); } catch (StringPoolException e) { throw new QueryException("Couldn't query constraint", e); } } /** * Create a string pool object to represent the beginning of a search range. * @param factory The string pool factory that will create the object * @param prefixNode The data for the object. * @param predType Predicate to indicate the type of data to get from the string pool. * @return An object representing the start of a range of data to get from the string pool. * @throws QueryException If the predicate indicates a URI but the prefix data is not a valid URI. */ private SPObject getStartObject(SPObjectFactory factory, Node prefixNode, long predType) throws QueryException { if (prefixNode instanceof Literal) { String prefix = ((Literal) prefixNode).getLexicalForm(); if (predType == mulgaraPrefix) { try { return factory.newSPURI(new URI(prefix)); } catch (URISyntaxException e) { throw new QueryException("Bad URI prefix provided: " + prefix + " (should this be using mulgara:stringPrefix?)"); } } else return factory.newSPString(prefix); } else { URI startPrefixUri = ((URIReference) prefixNode).getURI(); if (predType == mulgaraPrefix) return factory.newSPURI(startPrefixUri); else return factory.newSPString(startPrefixUri.toString()); } } /** * Create a string pool object to represent the end of a search range. * @param factory The string pool factory that will create the object * @param prefixNode The data for the object. * @param predType Predicate to indicate the type of data to get from the string pool. * @return An object representing the end of a range of data to get from the string pool. * @throws QueryException If the predicate indicates a URI but the prefix data is not a valid URI. */ private SPObject getEndObject(SPObjectFactory factory, Node prefixNode, long predType) throws QueryException { String prefix; if (prefixNode instanceof Literal) { prefix = ((Literal) prefixNode).getLexicalForm(); } else { prefix = ((URIReference) prefixNode).getURI().toString(); } if (predType == mulgaraPrefix) { try { return factory.newSPURI(new URI(prefix + Character.MAX_VALUE)); } catch (URISyntaxException e) { throw new QueryException("Bad URI prefix provided: " + prefix + " (should this be using mulgara:stringPrefix?)"); } } else return factory.newSPString(prefix + Character.MAX_VALUE); } /** * Close all sessions and factories used by this resolver. */ public void close() { // no-op } public void abort() {} }