/*
* 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):
* Copywrite in the compound-constraint syntax:
* The Australian Commonwealth Government
* Department of Defense
* Developed by Netymon Pty Ltd (mailto:mail@netymon.com)
* under contract 4500507038
* contributed to the Mulgara Project under the
* Mozilla Public License version 1.1
* per clause 4.1.3 and 4.1.4 of the above contract.
*
* [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.itql;
// Java 2 standard packages
import java.net.*;
import java.util.ArrayList;
import java.util.List;
// Third party packages
import org.apache.log4j.Logger; // Apache Log4J
// Locally written packages
// Automatically generated packages (SableCC)
import org.mulgara.itql.analysis.*;
import org.mulgara.itql.node.*;
import org.mulgara.query.*;
import org.mulgara.query.rdf.*;
/**
* A implementation of SableCC's Adapter which creates query constraint
* depending on the type of object given (visitor pattern). Tied heavily to
* the ItqlInterpreter.
*
* @created 2004-06-18
*
* @author Andrew Newman
*
* @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 ConstraintExpressionBuilder extends AnalysisAdapter {
/** The logger */
private final static Logger logger = Logger.getLogger(ConstraintExpressionBuilder.class.getName());
/** The internal result of parsing a constraint expression. */
private ConstraintExpression constraintExpression = null;
/** URI Syntax Exception - not null if exception occurred since last get. */
private URISyntaxException uriException = null;
/** Query Exception - not null if exception occurred since last get. */
private QueryException queryException = null;
/** The iTQL interpreter */
private SableCCInterpreter interpreter;
/**
* Create a new builder. Requires methods on the interpreter in order to
* function correctly.
*
* @param newInterpreter the interpreter to use.
*/
public ConstraintExpressionBuilder(SableCCInterpreter newInterpreter) {
interpreter = newInterpreter;
}
/**
* Returns the latest constraint expression or throws an exception if there
* was an error creating the last expression. Once called the constraint
* expression object and exceptions are nulled.
*
* @throws QueryException if constraint does not represent a valid query
* @throws URISyntaxException if the constraint contains a resource whose
* text violates <a href="http://www.isi.edu/in-notes/rfc2396.txt">RFC?2396</a>
*/
public ConstraintExpression getConstraintExpression() throws QueryException, URISyntaxException {
try {
ConstraintExpression tmpExpression = constraintExpression;
if (uriException != null) {
throw uriException;
} else if (queryException != null) {
throw queryException;
} else {
return tmpExpression;
}
} finally {
uriException = null;
queryException = null;
constraintExpression = null;
}
}
/**
* Sets constraint expression. Will set URIException or QueryException if
* an exception occurs.
*
* @param newConstraintExpression the new expression.
*/
private void setConstraintExpression(ConstraintExpression newConstraintExpression) {
constraintExpression = newConstraintExpression;
}
public void caseAConstraintConstraintFactor(AConstraintConstraintFactor rawConstraintFactor) {
if (logger.isDebugEnabled()) logger.debug("Found constraint constraint factor " + rawConstraintFactor);
// get the constraint
PConstraint constraint = ((AConstraintConstraintFactor)rawConstraintFactor).getConstraint();
if (logger.isDebugEnabled()) logger.debug("Found constraint " + constraint + ", resolving components");
// get the constraint's components
try {
Constraint tmpConstraint;
ConstraintElement subject = toConstraintElement(((AConstraint)constraint).getSubject());
ConstraintElement predicate = toConstraintElement(((AConstraint)constraint).getPredicate());
ConstraintElement object = toConstraintElement(((AConstraint)constraint).getObject());
if (logger.isDebugEnabled()) {
logger.debug("Found subject " + subject);
logger.debug("Found predicate " + predicate);
logger.debug("Found object " + object);
}
// check for an IN clause
AInClause inClause = (AInClause)((AConstraint)constraint).getInClause();
// bundle them into a constraint (order is probably important here...?)
if (inClause != null) {
if (logger.isDebugEnabled()) logger.debug("Found model " + inClause.getElement());
ConstraintElement model = toConstraintElement(inClause.getElement());
tmpConstraint = ConstraintFactory.newConstraint(subject, predicate, object, model);
} else {
tmpConstraint = ConstraintFactory.newConstraint(subject, predicate, object);
}
// Set new value.
if (logger.isDebugEnabled()) logger.debug("Setting constraint: " + tmpConstraint);
setConstraintExpression(tmpConstraint);
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a constraint expression. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintFactor the expression to create a constraint expression from.
*/
public void caseAExpressionConstraintFactor(AExpressionConstraintFactor rawConstraintFactor) {
try {
if (logger.isDebugEnabled()) logger.debug("Found factor expression constraint factor " + rawConstraintFactor);
// get the constraint expression
PConstraintExpression embeddedConstraintExpression =
((AExpressionConstraintFactor)rawConstraintFactor).getConstraintExpression();
if (logger.isDebugEnabled()) logger.debug("Recursing with constraint factor " + embeddedConstraintExpression);
// build the constraint expression
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
embeddedConstraintExpression.apply((Switch) builder);
setConstraintExpression(builder.getConstraintExpression());
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a transitive constraint. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintFactor a transitive constraint.
*/
public void caseATransitiveConstraintFactor(ATransitiveConstraintFactor rawConstraintFactor) {
try {
ConstraintExpression tmpConstraintExpression = null;
if (logger.isDebugEnabled()) logger.debug("Found factor of transitive expression" + rawConstraintFactor);
// get the constraint transitive
PTransitiveClause embeddedTransitiveConstraint =
((ATransitiveConstraintFactor) rawConstraintFactor).getTransitiveClause();
if (embeddedTransitiveConstraint instanceof ATransitive1TransitiveClause) {
// build the transitive constraint expression
tmpConstraintExpression = new SingleTransitiveConstraint(
buildConstraint(((ATransitive1TransitiveClause)
embeddedTransitiveConstraint).getConstraint()));
} else if (embeddedTransitiveConstraint instanceof ATransitive2TransitiveClause) {
// build the transitive constraint expression
ATransitive2TransitiveClause tmpClause = (ATransitive2TransitiveClause)embeddedTransitiveConstraint;
Constraint constraint1 = buildConstraint(tmpClause.getConstraint1());
Constraint constraint2 = buildConstraint(tmpClause.getConstraint2());
tmpConstraintExpression = new TransitiveConstraint(constraint1, constraint2);
}
setConstraintExpression(tmpConstraintExpression);
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a existential compound constraint.
*/
public void caseAExistentialConstraintFactor(AExistentialConstraintFactor rawFactor) {
if (logger.isDebugEnabled()) logger.debug("Found existential - constraint factor " + rawFactor);
try {
setConstraintExpression(
buildExistential(
interpreter.nextAnonVariable(),
rawFactor.getExistsExpression(),
(AInClause)rawFactor.getInClause()));
} catch (URISyntaxException eu) {
uriException = eu;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a concrete compound constraint.
*/
public void caseACompoundConstraintFactor(ACompoundConstraintFactor rawFactor) {
if (logger.isDebugEnabled()) logger.debug("Found compound - constraint factor " + rawFactor);
try {
setConstraintExpression(
buildExistential(
toConstraintElement(rawFactor.getSubject()),
rawFactor.getExistsExpression(),
(AInClause)rawFactor.getInClause()));
} catch (URISyntaxException eu) {
uriException = eu;
} catch (QueryException qe) {
queryException = qe;
}
}
public ConstraintExpression buildExistential(ConstraintElement subject, PExistsExpression rawTerm, AInClause in)
throws URISyntaxException, QueryException {
if (logger.isDebugEnabled()) logger.debug("building existential subject: " + subject + " term.class: " + rawTerm.getClass());
CompoundPredListBuilder builder = new CompoundPredListBuilder();
rawTerm.apply(builder);
if (logger.isDebugEnabled()) logger.debug("CompoundPredListBuilder built: " + builder.getPredLists());
ConstraintElement model = (in == null) ? null : toConstraintElement(in.getElement());
// forall predicates in list forall objects in pred's obj-list
// add new constraint(s,p,o) to argList.
List<ConstraintExpression> argList = new ArrayList<ConstraintExpression>();
for (CompoundPredicate plist: builder.getPredLists()) {
ConstraintElement predicate = toConstraintElement(plist.getPredicate());
for (PElement oelem: plist.getObjectList()) {
ConstraintElement object = toConstraintElement(oelem);
if (model == null) {
argList.add(ConstraintFactory.newConstraint(subject, predicate, object));
} else {
argList.add(ConstraintFactory.newConstraint(subject, predicate, object, model));
}
}
}
if (logger.isDebugEnabled()) logger.debug("Existential term = and(" + argList + ")");
return new ConstraintConjunction(argList);
}
/**
* Handle a walk constraint.
*
* @param rawConstraintFactor a walk constraint.
*/
public void caseAWalkConstraintFactor(AWalkConstraintFactor rawConstraintFactor) {
try {
// Build the walk constraint
AWalk1WalkClause embeddedWalkConstraint =
(AWalk1WalkClause)((AWalkConstraintFactor)rawConstraintFactor).getWalkClause();
ConstraintExpression tmpConstraintExpression = new WalkConstraint(
buildConstraint(embeddedWalkConstraint.getConstraint1()),
buildConstraint(embeddedWalkConstraint.getConstraint2()));
setConstraintExpression(tmpConstraintExpression);
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle an OR constraint. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintExpression an OR constraint.
*/
public void caseAOrConstraintExpression(AOrConstraintExpression rawConstraintExpression) {
try {
if (logger.isDebugEnabled()) logger.debug("Found OR constraint expression " +rawConstraintExpression);
// get the OR constraint expression
PConstraintExpression orConstraintExpression =
((AOrConstraintExpression)rawConstraintExpression).getConstraintExpression();
// get the constraint term
PConstraintTerm constraintTerm =
((AOrConstraintExpression)rawConstraintExpression).getConstraintTerm();
if (logger.isDebugEnabled()) logger.debug("Recursing with constraint expression " +
orConstraintExpression + " & constraint term " + constraintTerm);
// Construct a builder to process the constraints.
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
// get the LHS and RHS operands of the disjunction
orConstraintExpression.apply((Switch)builder);
ConstraintExpression lhs = builder.getConstraintExpression();
constraintTerm.apply((Switch)builder);
ConstraintExpression rhs = builder.getConstraintExpression();
if (logger.isDebugEnabled()) {
logger.debug("Resolved LHS disjunction operand " + lhs);
logger.debug("Resolved RHS disjunction operand " + rhs);
}
// apply the disjunction
setConstraintExpression(new ConstraintDisjunction(lhs, rhs));
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a term constraint. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintExpression a term constraint.
*/
public void caseATermConstraintExpression(ATermConstraintExpression rawConstraintExpression) {
try {
if (logger.isDebugEnabled()) logger.debug("Found term constraint expression " + rawConstraintExpression);
// get the constraint term
PConstraintTerm constraintTerm =
((ATermConstraintExpression) rawConstraintExpression).getConstraintTerm();
if (logger.isDebugEnabled()) logger.debug("Recursing with constraint term " + constraintTerm);
// Create a new builder.
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
constraintTerm.apply((Switch)builder);
// drill down into the constraint term
setConstraintExpression(builder.getConstraintExpression());
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a dterm constraint term. Will set URIException or QueryException
* if an exception occurs.
*
* @param rawConstraintTerm a dterm constraint term.
*/
public void caseADtermConstraintTerm(ADtermConstraintTerm rawConstraintTerm) {
try {
if (logger.isDebugEnabled()) logger.debug("Found dterm contraint term " + rawConstraintTerm);
// get the constraint factor
PConstraintDterm constraintDterm =
((ADtermConstraintTerm)rawConstraintTerm).getConstraintDterm();
ConstraintExpression tmpConstraintExpression = null;
// drill down into the constraint factor
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
constraintDterm.apply((Switch)builder);
tmpConstraintExpression = builder.getConstraintExpression();
setConstraintExpression(tmpConstraintExpression);
if (logger.isDebugEnabled()) {
logger.debug("Recursing with constraint factor " + constraintDterm);
logger.debug("Got: " + tmpConstraintExpression);
}
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a AND constraint term. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintTerm a AND constraint term.
*/
public void caseAAndConstraintTerm(AAndConstraintTerm rawConstraintTerm) {
try {
if (logger.isDebugEnabled()) logger.debug("Found AND contraint term " + rawConstraintTerm);
// get the constraint term
PConstraintTerm constraintTerm =
((AAndConstraintTerm) rawConstraintTerm).getConstraintTerm();
// get the constraint factor
PConstraintDterm constraintDterm =
((AAndConstraintTerm) rawConstraintTerm).getConstraintDterm();
if (logger.isDebugEnabled()) logger.debug("Recursing with constraint term " + constraintTerm +
" & constraint factor " + constraintDterm);
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
// get the LHS and RHS operands of the conjunction
constraintTerm.apply((Switch)builder);
ConstraintExpression lhs = builder.getConstraintExpression();
// Create another constraint builder and assign to RHS.
constraintDterm.apply((Switch) builder);
ConstraintExpression rhs = builder.getConstraintExpression();
if (logger.isDebugEnabled()) {
logger.debug("Resolved LHS conjunction operand " + lhs);
logger.debug("Resolved RHS conjunction operand " + rhs);
}
// apply the conjunction
setConstraintExpression(new ConstraintConjunction(lhs, rhs));
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a factor constraint dterm. Will set URIException or QueryException
* if an exception occurs.
*
* @param rawConstraintTerm a factor constraint dterm.
*/
public void caseAFactorConstraintDterm(AFactorConstraintDterm rawConstraintTerm) {
try {
logger.debug("Found factor contraint term " + rawConstraintTerm);
// get the constraint factor
PConstraintFactor constraintFactor =
((AFactorConstraintDterm) rawConstraintTerm).getConstraintFactor();
ConstraintExpression tmpConstraintExpression = null;
// drill down into the constraint factor
ConstraintExpressionBuilder builder =
new ConstraintExpressionBuilder(interpreter);
constraintFactor.apply((Switch) builder);
tmpConstraintExpression = builder.getConstraintExpression();
setConstraintExpression(tmpConstraintExpression);
if (logger.isDebugEnabled()) {
logger.debug("Recursing with constraint factor " + constraintFactor);
logger.debug("Got: " + tmpConstraintExpression);
}
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Handle a MINUS constraint dterm. Will set URIException or QueryException if
* an exception occurs.
*
* @param rawConstraintTerm a MINUS constraint dterm.
*/
public void caseAMinusConstraintDterm(AMinusConstraintDterm rawConstraintTerm) {
try {
if (logger.isDebugEnabled()) logger.debug("Found MINUS contraint dterm " + rawConstraintTerm);
// get the minuend expression
PConstraintDterm minuendExpr =
((AMinusConstraintDterm) rawConstraintTerm).getMinuend();
// get the subtrahend expression
PConstraintFactor subtrahendExpr =
((AMinusConstraintDterm) rawConstraintTerm).getSubtrahend();
if (logger.isDebugEnabled()) {
logger.debug("Recursing with minuend " + minuendExpr + " & subtrahend " + subtrahendExpr);
}
ConstraintExpressionBuilder builder = new ConstraintExpressionBuilder(interpreter);
// get the LHS and RHS operands of the conjunction
minuendExpr.apply((Switch)builder);
ConstraintExpression minuend = builder.getConstraintExpression();
// Create another constraint builder and assign to RHS.
subtrahendExpr.apply((Switch)builder);
ConstraintExpression subtrahend = builder.getConstraintExpression();
if (logger.isDebugEnabled()) {
logger.debug("Resolved minuend operand " + minuend);
logger.debug("Resolved subtrahend operand " + subtrahend);
}
// apply the conjunction
setConstraintExpression(new ConstraintDifference(minuend, subtrahend));
} catch (URISyntaxException use) {
uriException = use;
} catch (QueryException qe) {
queryException = qe;
}
}
/**
* Helper method used to build up a Constraint object from a SableCC
* PConstraint object.
*
* @param pconstraint The parser constraint object to convert.
* @return A Constraint object for use in transitive constraints.
* @throws QueryException if the constraint contains a NOT or IN section.
*/
Constraint buildConstraint(PConstraint pconstraint) throws QueryException,
URISyntaxException {
if (logger.isDebugEnabled()) logger.debug("Found constraint " + pconstraint + ", resolving components");
// get the constraint's components
ConstraintElement subject =
toConstraintElement(((AConstraint) pconstraint).getSubject());
ConstraintElement predicate =
toConstraintElement(((AConstraint) pconstraint).getPredicate());
ConstraintElement object =
toConstraintElement(((AConstraint) pconstraint).getObject());
if (logger.isDebugEnabled()) {
logger.debug("Found subject " + subject);
logger.debug("Found predicate " + predicate);
logger.debug("Found object " + object);
}
// check for an IN - this is illegal
if (((AConstraint)pconstraint).getInClause() != null) {
throw new QueryException("Illegal in clause on transitive constraint.");
}
// bundle them into a constraint (order is probably important here...?)
return ConstraintFactory.newConstraint(subject, predicate, object);
}
/**
* Constructs a {@link org.mulgara.query.ConstraintElement} from a
* {@link org.mulgara.itql.node.PElement}.
*
* @param element an element of a constraint (variable, resource or literal)
* @return a constraint element for the given <code>element</code>
* @throws QueryException if <code>element</code> is a
* {@link org.mulgara.itql.node.AResourceElement} whose text contains a
* <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified name</a>
* with a prefix not defined in the <code>aliasMap</code>
* @throws URISyntaxException if <code>element</code> is a
* {@link org.mulgara.itql.node.AResourceElement} whose text doesn't
* conform to
* <a href="http://www.isi.edu/in-notes/rfc2396.txt">RFC 2396</a>
*/
private ConstraintElement toConstraintElement(PElement element) throws QueryException, URISyntaxException {
// validate the element parameter
if (element == null) throw new IllegalArgumentException("Null \"element\" parameter");
if (logger.isDebugEnabled()) logger.debug("Resolving " + element + "to a constraint element");
// create a constraint element to return
ConstraintElement constraintElement = null;
// get the appropriate element type
if (element instanceof AVariableElement) {
// get the name of the variable
PVariable rawVariable = ((AVariableElement) element).getVariable();
String variableName = ((AVariable) rawVariable).getIdentifier().getText();
if (logger.isDebugEnabled()) logger.debug("Resolved " + element + " to variable " + variableName);
// create a new variable
constraintElement = new Variable(variableName);
} else if (element instanceof AResourceElement) {
// create a new resource
constraintElement = new URIReferenceImpl(interpreter.toURI(
((AResourceElement) element).getResource()), false);
} else if (element instanceof ALiteralElement) {
// create a new literal
constraintElement = interpreter.toLiteralImpl(((ALiteralElement) element).
getLiteral());
}
// return the constraint element
return constraintElement;
}
}