/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s):
* getModel() contributed by Netymon Pty Ltd on behalf of
* The Australian Commonwealth Government under contract 4500507038.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.resolver.nodetype;
// Java 2 standard packages
import java.net.*;
import java.util.*;
import javax.transaction.xa.XAResource;
// Third party packages
import org.apache.log4j.Logger;
// Locally written packages
import org.mulgara.query.*;
import org.mulgara.resolver.spi.*;
import org.mulgara.store.stringpool.SPObject;
import org.mulgara.store.stringpool.StringPoolException;
import org.mulgara.store.tuples.Tuples;
import org.mulgara.store.tuples.TuplesOperations;
/**
* Resolves constraints accessible through a session.
*
* @created 2004-10-27
* @author Paula Gearon
* @version $Revision: 1.10 $
* @modified $Date: 2005/05/15 01:04:25 $ @maintenanceAuthor $Author: pgearon $
* @company <a href="mailto:info@PIsoftware.com">Plugged In Software</a>
* @copyright © 2004 <a href="http://www.PIsoftware.com/">Plugged In
* Software Pty Ltd</a>
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class NodeTypeResolver implements Resolver
{
/** Logger. */
private static final Logger logger = Logger.getLogger(NodeTypeResolver.class.getName());
/** The session that this resolver is associated with. */
private final ResolverSession resolverSession;
/** The URI of the type describing node type graphs. */
private URI graphTypeURI;
/** The node for the type describing node type graphs. */
@SuppressWarnings("unused")
private long graphType;
/** The preallocated local node representing the rdf:type property. */
private long rdfType;
/** The local node represening the RDFS literal type. */
private long rdfsLiteral;
/** The local node represening the URI Reference type. */
private long mulgaraUriReference;
//
// 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
*/
NodeTypeResolver(
ResolverSession resolverSession,
Resolver systemResolver,
long rdfType,
long systemGraph,
long rdfsLiteral,
long mulgaraUriReference,
long graphType,
URI graphTypeURI
) 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.graphTypeURI = graphTypeURI;
this.graphType = graphType;
this.rdfType = rdfType;
this.rdfsLiteral = rdfsLiteral;
this.mulgaraUriReference = mulgaraUriReference;
}
//
// Methods implementing Resolver
//
/**
* Create a graph for node types.
*
* @param graph {@inheritDoc}.
* @param graphType {@inheritDoc}. This must be the URI for NodeType graphs.
*/
public void createModel(long graph, URI graphType) throws ResolverException {
if (logger.isDebugEnabled()) logger.debug("Create type graph " + graph);
if (!this.graphTypeURI.equals(graphType)) {
throw new ResolverException("Wrong graph type provided as a Node Type graph");
}
}
/**
* 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 graph.
* This is illegal for this graph type.
*
* @param graph the local node identifying an existing graph
* @param statements the {@link Statements} to insert into the
* <var>graph</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>graph</var>
*/
public void modifyModel(long graph, Statements statements, boolean occurs)
throws ResolverException
{
if (logger.isDebugEnabled()) {
logger.debug("Modify Node Type graph " + graph);
}
throw new ResolverException("Node Type graphs are read only");
}
/**
* Remove the cached graph containing the contents of a URL.
*/
public void removeModel(long graph) throws ResolverException
{
if (logger.isDebugEnabled()) {
logger.debug("Remove Node Type graph " + graph);
}
}
/**
* 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.warn("Ignoring solutions for non-local graph " + constraint);
return new EmptyResolution(constraint, false);
}
/*
// Convert the constraint's graph to a URI reference
URIReference graphUriRef;
long graph = ((LocalNode) constraint.getModel()).getValue();
if (findModelType(graph) != graphType) {
throw new QueryException("TypeGraph query was made on a graph which is not a Type Graph");
}
*/
try {
long property = ((LocalNode) constraint.getElement(1)).getValue();
// check the constraint for consistency
if (property != rdfType ||
constraint.getElement(2) instanceof Variable) {
throw new QueryException("Type resolver can only be used for type constraints: " + constraint);
}
long type = ((LocalNode) constraint.getElement(2)).getValue();
// Evaluate "type"
if (logger.isDebugEnabled()) {
logger.debug("Evaluating " + constraint.getElement(0) +
" has type " +
constraint.getElement(2));
}
ConstraintElement node = constraint.getElement(0);
assert node != null;
Tuples tuples;
if (node instanceof Variable) {
// select the type being extracted from the string pool
if (type == rdfsLiteral) {
// literals currently fall into 2 categories: typed and untyped
// extract each type into a tuples
Tuples[] t = new Tuples[2];
t[0] = resolverSession.findStringPoolType(SPObject.TypeCategory.UNTYPED_LITERAL, null);
t[1] = resolverSession.findStringPoolType(SPObject.TypeCategory.TYPED_LITERAL, null);
assert t[0] != null && t[1] != null;
t[0].renameVariables(constraint);
t[1].renameVariables(constraint);
long resultSize;
try {
// Get the size of the final result.
resultSize = t[0].getRowCount() + t[1].getRowCount();
if (logger.isDebugEnabled()) {
logger.debug("resolved " + t[0].getRowCount() + " untyped literal");
logger.debug("resolved " + t[1].getRowCount() + " typed literals");
}
// union the 2 tuples together
tuples = TuplesOperations.append(Arrays.asList(t));
t[0].close();
t[1].close();
} catch (TuplesException te) {
throw new QueryException("Unable to build result", te);
}
if (logger.isDebugEnabled()) {
try {
logger.debug("Appended tuples = " + tuples.getRowCount() + " (should be " +
resultSize + ")");
} catch (TuplesException te) {
logger.debug("Error getting the length of the tuples object");
}
}
} else if (type == mulgaraUriReference) {
// just need the URI type from the string pool
tuples = resolverSession.findStringPoolType(SPObject.TypeCategory.URI, null);
assert tuples != null;
tuples.renameVariables(constraint);
} else {
throw new QueryException("Type resolver can only find literals and URI References: " + constraint);
}
if (logger.isDebugEnabled()) {
logger.debug("Evaluated " + constraint.getElement(0) +
" has type " + constraint.getElement(2) + ": " + tuples);
}
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;
SPObject spo = resolverSession.findStringPoolObject(n.getValue());
if (spo == null) {
tuples = TuplesOperations.empty();
} else {
if (type == rdfsLiteral) {
if (
spo.getTypeCategory() == SPObject.TypeCategory.UNTYPED_LITERAL ||
spo.getTypeCategory() == SPObject.TypeCategory.TYPED_LITERAL
) {
tuples = TuplesOperations.unconstrained();
} else {
tuples = TuplesOperations.empty();
}
} else if (type == mulgaraUriReference) {
if (spo.getTypeCategory() == SPObject.TypeCategory.URI) {
tuples = TuplesOperations.unconstrained();
} else {
tuples = TuplesOperations.empty();
}
} else {
throw new QueryException("Type resolver can not find blank nodes: " + constraint);
}
}
}
// convert the tuples to a resolution
return new TuplesWrapperResolution(tuples, constraint);
} catch (StringPoolException e) {
throw new QueryException("Couldn't query constraint", e);
}
}
/**
* Close all sessions and factories used by this resolver.
*/
public void close() {
// no-op
}
public void abort() {}
}