/*
* 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):
* The copyright to this file is held by:
* The Australian Commonwealth Government
* Department of Defense
* Developed by Netymon Pty Ltd
* under contract 4500430665
* contributed to the Mulgara Project under the
* Mozilla Public License version 1.1
* per clause 4.1.3 of the above contract.
*
* SymbolicTransformationContext 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.util.*;
import java.net.URI;
// Third party packages
import org.apache.log4j.Logger;
import org.jrdf.graph.Literal;
import org.jrdf.graph.URIReference;
// Local packages
import org.mulgara.query.*;
import org.mulgara.query.rdf.URIReferenceImpl;
import org.mulgara.resolver.spi.AbstractSymbolicTransformer;
import org.mulgara.resolver.spi.SymbolicTransformationContext;
import org.mulgara.resolver.spi.SymbolicTransformationException;
/**
* A transformation rule that converts XSD ordering constraints into
* {@link IntervalConstraint}s.
*
* @created 2005-05-12
* @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a>
* @version $Revision: 1.3 $
* @modified $Date: 2005/06/09 09:26:20 $ by $Author: raboczi $
* @maintenanceAuthor $Author: raboczi $
* @copyright ©2005 <a href="http://www.defence.gov.au/">
* Australian Commonwealth Government, Department of Defence</a>
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
class IntervalTransformation extends AbstractSymbolicTransformer {
/** Logger. */
private static final Logger logger = Logger.getLogger(IntervalTransformation.class);
/**
* The node representing the < relation between <tt>xsd:double</tt>s.
*/
private URIReferenceImpl lessThan;
/**
* The node representing the > relation between <tt>xsd:double</tt>s.
*/
private URIReferenceImpl greaterThan;
/**
* The model type URI identifying the XSD Resolver.
*/
private URI modelTypeURI;
//
// Constructor
//
/**
* Sole constructor.
*
* @param lessThan property representing < for <tt>xsd:double</tt>s,
* never <code>null</code>
* @param greaterThan property representing > for <tt>xsd:double</tt>s,
* never <code>null</code>
*/
IntervalTransformation(URI modelTypeURI, URIReferenceImpl lessThan, URIReferenceImpl greaterThan) {
assert lessThan != null;
assert greaterThan != null;
// Initialize fields
this.modelTypeURI = modelTypeURI;
this.greaterThan = greaterThan;
this.lessThan = lessThan;
}
@Override
protected ConstraintExpression transformOperation(SymbolicTransformationContext context,
ConstraintOperation expr)
throws SymbolicTransformationException {
if (expr instanceof ConstraintConjunction)
return transformConjunction(context, (ConstraintConjunction)expr);
return super.transformOperation(context, expr);
}
private ConstraintExpression transformConjunction(SymbolicTransformationContext context, ConstraintConjunction constraintConjunction)
throws SymbolicTransformationException {
if (logger.isDebugEnabled()) {
logger.debug("Transforming conjunction " + constraintConjunction);
}
assert constraintConjunction != null;
List<ConstraintExpression> elements = constraintConjunction.getElements();
List<ConstraintExpression> newElements = new ArrayList<ConstraintExpression>(elements.size());
boolean modified = false;
// This map is keyed on Variable instances, and maps to the cumulative
// IntervalConstraint assocated with that variable
Map<Variable,IntervalConstraint> map = new HashMap<Variable,IntervalConstraint>();
for (Iterator<ConstraintExpression> i = elements.iterator(); i.hasNext();) {
ConstraintExpression element = i.next();
// Recursively transform the element
ConstraintExpression transformedElement = transformExpression(context, element);
if (transformedElement != element) {
if (logger.isDebugEnabled()) {
logger.debug("Recursively transformed " + element +
" to " + transformedElement);
}
modified = true;
element = transformedElement;
}
// Merge any IntervalConstraints
if (!(element instanceof IntervalConstraint)) {
if (logger.isDebugEnabled()) {
logger.debug("Unknown conjunction element: " + element + " type " + element.getClass());
}
newElements.add(element);
} else {
IntervalConstraint interval = (IntervalConstraint) element;
IntervalConstraint cumulative = map.get(interval.getVariable());
if (cumulative == null) {
cumulative = interval;
if (logger.isDebugEnabled()) {
logger.debug("Initial interval constraint: " + cumulative);
}
} else {
cumulative = cumulative.conjoin(interval);
modified = true;
if (logger.isDebugEnabled()) {
logger.debug("Cumulative interval constraint: " + cumulative);
}
}
assert cumulative != null;
map.put(interval.getVariable(), cumulative);
}
}
// If any intervals were extracted or merged, return the new expression
if (!modified) {
if (logger.isDebugEnabled()) {
logger.debug("Conjunction not modified");
}
return constraintConjunction;
} else {
newElements.addAll(map.values());
// Convert the list of conjoined terms into a ConstraintExpression
ConstraintExpression result;
switch (newElements.size()) {
case 0: result = ConstraintFalse.INSTANCE; break;
case 1: result = (ConstraintExpression) newElements.get(0); break;
default: result = new ConstraintConjunction(newElements);
}
assert result != null;
if (logger.isDebugEnabled()) {
logger.debug("Conjunction modified returning " + result);
}
return result;
}
}
@Override
protected ConstraintExpression transformConstraint(SymbolicTransformationContext context,
Constraint constraint)
throws SymbolicTransformationException {
assert constraint != null;
if (constraint instanceof IntervalConstraint) return constraint;
Variable variable;
boolean boundedBelow;
Bound bound;
if (logger.isDebugEnabled()) {
logger.debug("Transforming Constraint: " + constraint);
}
// Confirm model is of type XSDModel.
try {
ConstraintElement modelElement = constraint.getModel();
if (!(modelElement instanceof URIReference)) {
logger.debug("model not URIReference; cannot participate in transform, returning original");
return constraint;
}
URI modelURI = ((URIReference)modelElement).getURI();
URI modelTypeURI = context.mapToModelTypeURI(modelURI);
if (!this.modelTypeURI.equals(modelTypeURI)) {
logger.debug("model: " + modelURI + " is of type " + modelTypeURI + " not " + this.modelTypeURI + " ignoring constraint, returning original");
return constraint;
}
} catch (QueryException eq) {
throw new SymbolicTransformationException("Unable to check model on constraint", eq);
}
logger.debug("Graph suitable for IntervalTransformation");
// Figure out the direction of bounding, assuming [$var op value] order
if (constraint.getElement(1).equals(lessThan)) {
boundedBelow = false;
} else if (constraint.getElement(1).equals(greaterThan)) {
boundedBelow = true;
} else {
logger.debug("Predicate not recognised by IntervalTransformation");
return constraint;
}
// Determine whether we have a [$var op value] or [value op $var] form
if (constraint.getElement(0) instanceof Variable) {
if (constraint.getElement(2) instanceof Variable) {
logger.debug("Both Subject and Object are Variables, returning original");
return constraint;
} else {
variable = (Variable) constraint.getElement(0);
double value = Double.parseDouble(
((Literal) constraint.getElement(2)).getLexicalForm()
);
bound = new Bound(value, false);
}
} else {
if (!(constraint.getElement(2) instanceof Variable)) {
logger.debug("Neither Subject nor Object are Variables, returning original");
return constraint;
} else {
variable = (Variable) constraint.getElement(2);
double value = Double.parseDouble(
((Literal) constraint.getElement(0)).getLexicalForm()
);
bound = new Bound(value, false);
boundedBelow = !boundedBelow; // reverse the comparison
}
}
// Generate the constraint
return new IntervalConstraint(
variable,
boundedBelow ? bound : null,
boundedBelow ? null : bound,
(URIReference)constraint.getModel()
);
}
}