/* * 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.xsd; // Java 2 standard packages; import java.net.*; import java.text.ParseException; import java.util.*; // Third party packages import org.apache.log4j.Logger; import javax.transaction.xa.XAResource; // Locally written packages import org.mulgara.content.Content; import org.mulgara.query.*; import org.mulgara.query.rdf.XSD; import org.mulgara.resolver.spi.*; import org.mulgara.store.stringpool.*; import org.mulgara.store.stringpool.SPObject.TypeCategory; import org.mulgara.store.tuples.Tuples; import org.mulgara.store.tuples.TuplesOperations; /** * Resolves XML Schema datatyping constraints from the Mulgara string pool. * * @created 2004-10-28 * * @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a> * @author Mark Ludlow * * @version $Revision: 1.10 $ * * @modified $Date: 2005/05/02 20:07:59 $ by $Author: raboczi $ * * @maintenanceAuthor $Author: raboczi $ * * @company <a href="mailto:info@PIsoftware.com">Plugged In Software</a> * * @copyright © 2003 <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 XSDResolver implements Resolver { /** Logger */ private static final Logger logger = Logger.getLogger(XSDResolver.class); /** * This flag indicates that we should reinterpret literals that appear as * arguments to binary operations to be of the type which that operation * supports. * * Without his flag set, a more strict view is taken when an inappropriate * datatype is passed and a {@link QueryException} will be thrown. */ private static final boolean PERMISSIVE_PARAMETER_KLUDGE = true; /** Description of the Field */ static long MULGARA_AFTER; /** Description of the Field */ static long MULGARA_BEFORE; /** Description of the Field */ static long MULGARA_LT; /** Description of the Field */ static long MULGARA_GT; /** Description of equals field */ static long MULGARA_IS; /** Description of the Field */ static final Map<LocalNode,LocalNode> oppositePropertyMap = new HashMap<LocalNode,LocalNode>(); /** The session that this resolver is associated with */ private final ResolverSession resolverSession; /** * Construct a local query. * * @param resolverSession the session which this query is local to * @param systemResolver The resolver used to resolver system model queries * @param rdfType The type of the model * @param systemModel The model which represents the system model * * @throws IllegalArgumentException if <var>variableList</var> is * <code>null</code> or empty */ XSDResolver(ResolverSession resolverSession, Resolver systemResolver, long rdfType, long systemModel) throws ResolverFactoryException { // Validate "resolverSession" parameter if (resolverSession == null) { throw new IllegalArgumentException("Null \"resolverSession\" parameter"); } // Initialize fields this.resolverSession = resolverSession; } /** * @param graphResource a model identified in the <code>FROM</code> clause * @return <code>true</code> only if this resource is the unique XML Schema * model * @throws QueryException always, because it's not implemented * @deprecated XSDResolver.canResolve is not implemented */ public boolean canResolve(GraphResource graphResource) throws QueryException { throw new QueryException(getClass() + ".canResolve not implemented"); } /** * Create a model by creating empty {@link Content}. * * @param model {@inheritDoc}. In this case it should always be a URL * referencing {@link Content} outside the database. * * @param modelTypeURI {@inheritDoc}. This field is ignored, because the * {@link Content} is external. */ public void createModel(long model, URI modelTypeURI) throws ResolverException, LocalizeException { if (logger.isDebugEnabled()) { logger.debug("Create content model " + model); } } /** * @return a {@link DummyXAResource} with a 10 second transaction timeout */ public XAResource getXAResource() { return new DummyXAResource(10); } /** * Insert or delete RDF statements from a model stored in a file. */ public void modifyModel(long model, Statements statements, boolean occurs) throws ResolverException { if (logger.isDebugEnabled()) { logger.debug("Modify external document model " + model); } throw new ResolverException("Modification of external documents not " + "implemented"); } /** * Remove the file containing the model. */ public void removeModel(long model) throws ResolverException { if (logger.isDebugEnabled()) { logger.debug("Remove model " + model); } } /** * Resolve a constraint based on special knowledge of a datatype. * * @param constraint the constraint to resolve; this has the side effect of * setting the constraint's row count estimate * @return the statements which are true of the datatype, but only of * instances of the datatype present in the string pool */ public Resolution resolve(Constraint constraint) throws QueryException { if (logger.isDebugEnabled()) { logger.debug("!! Resolving " + constraint); } if (constraint.getModel() instanceof Variable) { if (logger.isDebugEnabled()) logger.debug("Ignoring solutions for " + constraint); return new EmptyResolution(constraint, false); } try { Tuples gNodeTuples = null; // Reorder constraints from "value op var" to "var op value" if (!(constraint.getElement(0) instanceof Variable) && oppositePropertyMap.keySet().contains(constraint.getElement(1)) && constraint.getElement(2) instanceof Variable) { constraint = new ConstraintImpl(constraint.getElement(2), oppositePropertyMap.get(constraint.getElement(1)), constraint.getElement(0), constraint.getModel()); } long property = ((LocalNode) constraint.getElement(1)).getValue(); if ((constraint.getElement(0) instanceof Variable) && !(constraint.getElement(2) instanceof Variable)) { if (property == MULGARA_LT) { if (logger.isDebugEnabled()) { logger.debug("Evaluating " + constraint.getElement(0) + " less than " + constraint.getElement(2)); } // Evaluate "less than" for xsd:double gNodeTuples = resolverSession.findStringPoolRange( null, // low value true, // include low value getBoundDouble(constraint.getElement(2)), // high value false // exclude high value ); } else if (property == MULGARA_GT) { if (logger.isDebugEnabled()) { logger.debug("Evaluating " + constraint.getElement(0) + "(" + constraint.getElement(0).getClass() + ") " + " greater than " + constraint.getElement(2) + "(" + constraint.getElement(2).getClass() + ") "); } // Evaluate "greater than" for xsd:double gNodeTuples = resolverSession.findStringPoolRange( getBoundDouble(constraint.getElement(2)), // low value false, // exclude low value null, // high value true // include high value ); } else if (property == MULGARA_BEFORE) { if (logger.isDebugEnabled()) { logger.debug("Evaluating " + constraint.getElement(0) + " before " + constraint.getElement(2)); } // Evaluate "before" for xsd:date gNodeTuples = resolverSession.findStringPoolRange( null, // lowest value true, // include low value getBoundDate(constraint.getElement(2)), // high value false // exclude high value ); } else if (property == MULGARA_AFTER) { // Evaluate "less than" for xsd:double if (logger.isDebugEnabled()) { logger.debug("Evaluating " + constraint.getElement(0) + " after " + constraint.getElement(2)); } // Evaluate "after" for xsd:date gNodeTuples = resolverSession.findStringPoolRange( getBoundDate(constraint.getElement(2)), // low value false, // exclude low value null, // high value true // include high value ); } else { throw new QueryException("Unsupported XSD predicate: " + property); } assert gNodeTuples != null; if (logger.isDebugEnabled()) { logger.debug("-- Renaming tuples of type: " + gNodeTuples.getClass()); } // Generate the solution gNodeTuples.renameVariables(constraint); if (logger.isDebugEnabled()) { logger.debug("Finished renaming."); logger.debug("Evaluated " + constraint.getElement(0) + " less than " + constraint.getElement(2) + ": " + gNodeTuples); } return new XSDResolution(constraint, gNodeTuples); } else if (!(constraint.getElement(0) instanceof Variable) && !(constraint.getElement(2) instanceof Variable)) { // Evaluate the cases where neither subject nor object are variables boolean condition; if (property == MULGARA_LT) { SPObject lhs = getBoundDouble(constraint.getElement(0)); SPObject rhs = getBoundDouble(constraint.getElement(2)); condition = lhs.compareTo(rhs) < 0; } else if (property == MULGARA_GT) { SPObject lhs = getBoundDouble(constraint.getElement(0)); SPObject rhs = getBoundDouble(constraint.getElement(2)); condition = lhs.compareTo(rhs) > 0; } else if (property == MULGARA_AFTER) { SPObject lhs = getBoundDate(constraint.getElement(0)); SPObject rhs = getBoundDate(constraint.getElement(2)); condition = lhs.compareTo(rhs) > 0; } else if (property == MULGARA_BEFORE) { SPObject lhs = getBoundDate(constraint.getElement(0)); SPObject rhs = getBoundDate(constraint.getElement(2)); condition = lhs.compareTo(rhs) < 0; } else { throw new QueryException("Invalid property: " + constraint); } return condition ? new XSDResolution(constraint, TuplesOperations.unconstrained()) : new XSDResolution(constraint, TuplesOperations.empty()); } else if (constraint.getElement(0) instanceof Variable && constraint.getElement(2) instanceof Variable) { throw new QueryException("Can't resolve constraint with 2 variables: " + constraint); } else { throw new Error("Don't know how to handle constraint: " + constraint); } } catch (TuplesException e) { throw new QueryException("Couldn't query constraint", e); } catch (ParseException e) { throw new QueryException("Couldn't query constraint", e); } catch (StringPoolException e) { throw new QueryException("Couldn't query constraint", e); } } /** * Gets the BoundDate attribute of the XSDTuples object * * @param constraintElement PARAMETER TO DO * @return The BoundDate value * @throws ParseException EXCEPTION TO DO * @throws QueryException EXCEPTION TO DO * @throws StringPoolException EXCEPTION TO DO */ private SPObject getBoundDate(ConstraintElement constraintElement) throws ParseException, QueryException, StringPoolException { if (constraintElement instanceof LocalNode) { SPObject spo = resolverSession.findStringPoolObject(((LocalNode)constraintElement).getValue()); if (PERMISSIVE_PARAMETER_KLUDGE && spo.getTypeCategory() == TypeCategory.UNTYPED_LITERAL) { // Try to parse the string as a date or a dateTime. try { return resolverSession.getSPObjectFactory().newSPTypedLiteral(spo.getLexicalForm(), XSD.DATE_URI); } catch (IllegalArgumentException e) { try { return resolverSession.getSPObjectFactory().newSPTypedLiteral(spo.getLexicalForm(), XSD.DATE_TIME_URI); } catch (IllegalArgumentException e2) { // fall through and let the string be returned unchanged. } } } return spo; } else { throw new Error("Unsupported constraint element: " + constraintElement); } } /** * Gets the BoundDouble attribute of the XSDTuples object * * @param constraintElement PARAMETER TO DO * @return The BoundDouble value * @throws QueryException EXCEPTION TO DO * @throws StringPoolException EXCEPTION TO DO */ private SPObject getBoundDouble(ConstraintElement constraintElement) throws QueryException, StringPoolException { if (constraintElement instanceof LocalNode) { SPObject spo = resolverSession.findStringPoolObject(((LocalNode) constraintElement).getValue()); if (logger.isDebugEnabled()) { logger.debug("!! Local node constraint element: " + ((LocalNode) constraintElement).getValue()); logger.debug("!! SPObject returned: " + spo); } if (PERMISSIVE_PARAMETER_KLUDGE && spo.getTypeCategory() == TypeCategory.UNTYPED_LITERAL) { // Try to parse the string as a Double. try { return resolverSession.getSPObjectFactory().newSPTypedLiteral(spo.getLexicalForm(), XSD.DOUBLE_URI); } catch (NumberFormatException e) { // fall through and let the string be returned unchanged. } } return spo; } else { throw new Error("Unsupported constraint element: " + constraintElement); } } public void abort() {} }