[Mulgara-svn] r804 - in branches/mgr-61-sparql/src/jar: . sparql-interpreter sparql-interpreter/java sparql-interpreter/java/org sparql-interpreter/java/org/mulgara sparql-interpreter/java/org/mulgara/sparql

pag at mulgara.org pag at mulgara.org
Tue Apr 22 06:26:40 UTC 2008


Author: pag
Date: 2008-04-21 23:26:40 -0700 (Mon, 21 Apr 2008)
New Revision: 804

Added:
   branches/mgr-61-sparql/src/jar/sparql-interpreter/
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/PatternMapper.java
   branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/SparqlInterpreter.java
Log:
Initial commit. This is still a work in progress. There is no conversion of filters yet, and no integration with Ant. A new mapping class is needed in this package, and a new Constraint for setting "IN" values has to go into org.mulgara.query

Added: branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/PatternMapper.java
===================================================================
--- branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/PatternMapper.java	                        (rev 0)
+++ branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/PatternMapper.java	2008-04-22 06:26:40 UTC (rev 804)
@@ -0,0 +1,290 @@
+/**
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * 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.
+ */
+package org.mulgara.sparql;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.mulgara.sparql.parser.cst.BlankNode;
+import org.mulgara.sparql.parser.cst.BooleanLiteral;
+import org.mulgara.sparql.parser.cst.DecimalLiteral;
+import org.mulgara.sparql.parser.cst.DoubleLiteral;
+import org.mulgara.sparql.parser.cst.EmptyGraphPattern;
+import org.mulgara.sparql.parser.cst.Expression;
+import org.mulgara.sparql.parser.cst.GraphPatternConjunction;
+import org.mulgara.sparql.parser.cst.GraphPatternDisjunction;
+import org.mulgara.sparql.parser.cst.GraphPatternOptional;
+import org.mulgara.sparql.parser.cst.GroupGraphPattern;
+import org.mulgara.sparql.parser.cst.IRIReference;
+import org.mulgara.sparql.parser.cst.IntegerLiteral;
+import org.mulgara.sparql.parser.cst.Node;
+import org.mulgara.sparql.parser.cst.RDFLiteral;
+import org.mulgara.sparql.parser.cst.Triple;
+import org.mulgara.sparql.parser.cst.TripleList;
+import org.mulgara.query.ConstraintConjunction;
+import org.mulgara.query.ConstraintDisjunction;
+import org.mulgara.query.ConstraintElement;
+import org.mulgara.query.ConstraintExpression;
+import org.mulgara.query.ConstraintFalse;
+import org.mulgara.query.ConstraintImpl;
+import org.mulgara.query.ConstraintOptionalJoin;
+import org.mulgara.query.Variable;
+import org.mulgara.query.rdf.LiteralImpl;
+import org.mulgara.query.rdf.URIReferenceImpl;
+import org.mulgara.query.rdf.XSD;
+
+/**
+ * This object maps a {@link GroupGraphPattern} into a {@link ConstraintExpresion}.
+ *
+ * @created Apr 21, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class PatternMapper {
+
+  /** The pattern to start the mapping on. */
+  GroupGraphPattern startPattern;
+
+  /** An accumulating list of variables that are used in GRAPH patterns. */
+  Set<Variable> graphVars = new HashSet<Variable>();
+
+  /** An accumulating list of URIs that are used in GRAPH patterns. */
+  Set<URI> graphUris = new HashSet<URI>();
+
+  /**
+   * Create a new mapper for a given graph pattern.
+   * @param pattern The graph pattern to be mapped.
+   */
+  PatternMapper(GroupGraphPattern pattern) {
+    startPattern = pattern;
+    initMapping();
+  }
+
+  /**
+   * Get the set of variables that were employed as graphs in GRAPH patterns.
+   * @return The set of variables used in GRAPH expressions.
+   */
+  Set<Variable> getGraphVars() {
+    return graphVars;
+  }
+
+  /**
+   * Perform the mapping of the graph pattern and return the results as a {@link ConstraintExpression}.
+   * @return The mapped constraint expression.
+   */
+  ConstraintExpression mapToConstraints() {
+    return map(startPattern);
+  }
+
+  /**
+   * Converts a pattern to the matching constraint type.
+   * @param pattern The pattern to convert.
+   * @return The new ConstraintExpression which matches the pattern.
+   */
+  private ConstraintExpression map(GroupGraphPattern pattern) {
+    PatternToConstraintMapper<? extends GroupGraphPattern> cons = constructors.get(pattern.getClass());
+    if (cons == null) throw new UnsupportedOperationException("Unknown SPARQL pattern: " + pattern.getClass().getSimpleName());
+    ConstraintExpression result = cons.map(pattern);
+    result = applyFilter(result, pattern.getFilter());
+    result = applyGraph(result, pattern.getGraph());
+    return result;
+  }
+
+  /**
+   * Apply a FILTER to a constraint expression.
+   * @param constraint The expression to be filtered.
+   * @param filter The filter to be wrapped around this constraint.
+   * @return The filtered version of the constraint.
+   */
+  private ConstraintExpression applyFilter(ConstraintExpression constraint, Expression filter) {
+    if (filter == null) return constraint;
+    // TODO create a filter builder and wrap the constraint witha FilteredConstraint
+    return constraint;
+  }
+
+  /**
+   * Apply the parameter of a GRAPH modifier to a constraint expression.
+   * @param constraint The expression to be updated.
+   * @param graph The parameter of the GRAPH expression that is to be propagated through the constraint.
+   * @return The modified version of the constraint.
+   */
+  private ConstraintExpression applyGraph(ConstraintExpression constraint, Expression graph) {
+    if (graph == null) return constraint;
+    // graph is a Variable or IRIReference
+    if (graph instanceof org.mulgara.sparql.parser.cst.Variable) {
+      org.mulgara.sparql.parser.cst.Variable v = (org.mulgara.sparql.parser.cst.Variable)graph;
+      // remember this is variable to be bound to the FROM NAMED values
+      Variable var = new Variable(v.getName());
+      graphVars.add(var);
+      // TODO: wrap the constraint in a new ConstraintIn
+      // constraint = new ConstraintIn(constraint, var);
+    } else if (graph instanceof IRIReference) {
+      // store this reference as a value that should be in the FROM NAMED list
+      URI ref = ((IRIReference)graph).getUri();
+      graphUris.add(ref);
+      // TODO: wrap the constraint in a new ConstraintIn
+      // constraint = new ConstraintIn(constraint, new URIReferenceImpl(ref));
+    } else {
+      throw new IllegalArgumentException("Illegal argument in a GRAPH expression: " + graph.getClass().getSimpleName());
+    }
+    return constraint;
+  }
+
+  /**
+   * A case analysis to convert simple types into {@link ConstraintElement}s.
+   * @param n The {@link Node} to convert to a ConstraintElement.
+   * @return A new constraint element that matches the node n.
+   */
+  private ConstraintElement convertElement(Node n) {
+    if (n instanceof org.mulgara.sparql.parser.cst.Variable) {
+      return new Variable(((org.mulgara.sparql.parser.cst.Variable)n).getName());
+    }
+    if (n instanceof IRIReference) return new URIReferenceImpl(((IRIReference)n).getUri());
+    if (n instanceof BlankNode) return new Variable(((BlankNode)n).getLabel());
+    if (n instanceof RDFLiteral) {
+      RDFLiteral lit = (RDFLiteral)n;
+      if (lit.isTyped()) return new LiteralImpl(lit.getValue(), lit.getDatatype().getUri());
+      if (lit.isLanguageCoded()) return new LiteralImpl(lit.getValue(), lit.getLanguage());
+      return new LiteralImpl(lit.getValue());
+    }
+    // integer, decimal, double, boolean
+    if (n instanceof IntegerLiteral) return new LiteralImpl(((IntegerLiteral)n).getValue().toString(), XSD.INT_URI);
+    if (n instanceof DecimalLiteral) return new LiteralImpl(((IntegerLiteral)n).getValue().toString(), XSD.DECIMAL_URI);
+    if (n instanceof DoubleLiteral) return new LiteralImpl(((IntegerLiteral)n).getValue().toString(), XSD.DOUBLE_URI);
+    if (n instanceof BooleanLiteral) return new LiteralImpl(((IntegerLiteral)n).getValue().toString(), XSD.BOOLEAN_URI);
+    // don't know what to make of this
+    throw new UnsupportedOperationException("Unhandled data type in triple: " + n.getClass().getSimpleName());
+  }
+
+  /**
+   * Converts a Triple from the CST into a ConstraintImpl in the AST.
+   * @param t The triple to convert.
+   * @return The new constraint.
+   */
+  private ConstraintImpl newConstraintImpl(Triple t) {
+    ConstraintElement s = convertElement(t.getSubject());
+    ConstraintElement p = convertElement(t.getPredicate());
+    ConstraintElement o = convertElement(t.getObject());
+    return new ConstraintImpl(s, p, o);
+  }
+
+  /** A mapping of pattern types to constructors for the objects they map to. */
+  private Map<Class<? extends GroupGraphPattern>,PatternToConstraintMapper<? extends GroupGraphPattern>> constructors = new HashMap<Class<? extends GroupGraphPattern>,PatternToConstraintMapper<? extends GroupGraphPattern>>();
+
+  /**
+   * The class for the mapping of {@link GroupGraphPattern}s to {@link ConstraintExpression}s.
+   * This class and extending classes are not static, as the classes will be call back into the
+   * outer class to recurse down the CST.
+   * The reason this is an abstract class instead of an interface is so map(T) can be set
+   * to accept the general GroupGraphPattern and do the cast. This was not possible outside
+   * of the context of the generic type T.
+   */
+  private abstract class PatternToConstraintMapper<T extends GroupGraphPattern> {
+    @SuppressWarnings("unchecked")
+    public ConstraintExpression map(GroupGraphPattern pattern) { return typedMap((T)pattern); }
+    /**
+     * Convert a GroupGraphPattern to a ConstraintExpression for Mulgara.
+     * @param pattern The pattern to convert. Should be specific to the mapper.
+     * @return The constraint expression relevant to the mapper.
+     */
+    abstract ConstraintExpression typedMap(T pattern);
+    /** Identify the class to be mapped by the extension. */
+    public abstract Class<T> getMapType();
+  }
+
+  /**
+   * Utility method to add a pattern mapper to the map, keyed on the class it maps.
+   * @param mapper The mapper to add to the map.
+   */
+  void addToMap(PatternToConstraintMapper<? extends GroupGraphPattern> mapper) {
+    constructors.put(mapper.getMapType(), mapper);
+  }
+
+  /**
+   * Initialize the mapping of patterns to the constraint builders.
+   * This is not static in order to avoid passing "this" through to the methods
+   * on each of the implementing mapper classes.
+   */
+  private void initMapping() {
+    addToMap(new EmptyGraphPatternToConstraint());
+    addToMap(new GraphPatternConjunctionToConstraint());
+    addToMap(new GraphPatternDisjunctionToConstraint());
+    addToMap(new GraphPatternOptionalToConstraint());
+    addToMap(new TripleToConstraint());
+    addToMap(new TripleListToConstraint());
+  }
+
+  /** Map the empty graph pattern to a constraint that always resolves to nothing. */
+  private class EmptyGraphPatternToConstraint extends PatternToConstraintMapper<EmptyGraphPattern> {
+    public Class<EmptyGraphPattern> getMapType() { return EmptyGraphPattern.class; }
+    ConstraintExpression typedMap(EmptyGraphPattern pattern) {
+      return ConstraintFalse.INSTANCE;
+    }
+  }
+
+  /** Map the conjunctions to ConstraintConjunction. */
+  private class GraphPatternConjunctionToConstraint extends PatternToConstraintMapper<GraphPatternConjunction> {
+    public Class<GraphPatternConjunction> getMapType() { return GraphPatternConjunction.class; }
+    ConstraintExpression typedMap(GraphPatternConjunction pattern) {
+      List<GroupGraphPattern> list = pattern.getElements();
+      List<ConstraintExpression> newList = new ArrayList<ConstraintExpression>(list.size());
+      for (GroupGraphPattern p: list) newList.add(map(p));
+      return new ConstraintConjunction(newList);
+    }
+  }
+
+  /** Map the disjunctions to ConstraintConjunction. */
+  private class GraphPatternDisjunctionToConstraint extends PatternToConstraintMapper<GraphPatternDisjunction> {
+    public Class<GraphPatternDisjunction> getMapType() { return GraphPatternDisjunction.class; }
+    ConstraintExpression typedMap(GraphPatternDisjunction pattern) {
+      List<GroupGraphPattern> list = pattern.getElements();
+      List<ConstraintExpression> newList = new ArrayList<ConstraintExpression>(list.size());
+      for (GroupGraphPattern p: list) newList.add(map(p));
+      return new ConstraintDisjunction(newList);
+    }
+  }
+
+  /** Map the optional patterns to ConstraintOptional. */
+  private class GraphPatternOptionalToConstraint extends PatternToConstraintMapper<GraphPatternOptional> {
+    public Class<GraphPatternOptional> getMapType() { return GraphPatternOptional.class; }
+    ConstraintExpression typedMap(GraphPatternOptional pattern) {
+      return new ConstraintOptionalJoin(map(pattern.getMain()), map(pattern.getOptional()));
+    }
+  }
+
+  /** Map the triple patterns to ConstraintImpl. */
+  private class TripleToConstraint extends PatternToConstraintMapper<Triple> {
+    public Class<Triple> getMapType() { return Triple.class; }
+    ConstraintExpression typedMap(Triple pattern) {
+      return newConstraintImpl(pattern);
+    }
+  }
+
+  /** Map the lists of triple patterns to ConstraintConjunctions on ConstraintImpl. */
+  private class TripleListToConstraint extends PatternToConstraintMapper<TripleList> {
+    public Class<TripleList> getMapType() { return TripleList.class; }
+    @SuppressWarnings("unchecked")
+    ConstraintExpression typedMap(TripleList pattern) {
+      List<Triple> triples = (List<Triple>)pattern.getElements();
+      List<ConstraintExpression> constraints = new ArrayList<ConstraintExpression>(triples.size());
+      for (Triple t: triples) constraints.add(newConstraintImpl(t));
+      return new ConstraintConjunction(constraints);
+    }
+  }
+
+}

Added: branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/SparqlInterpreter.java
===================================================================
--- branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/SparqlInterpreter.java	                        (rev 0)
+++ branches/mgr-61-sparql/src/jar/sparql-interpreter/java/org/mulgara/sparql/SparqlInterpreter.java	2008-04-22 06:26:40 UTC (rev 804)
@@ -0,0 +1,278 @@
+/**
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * 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.
+ */
+package org.mulgara.sparql;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.mulgara.parser.Interpreter;
+import org.mulgara.parser.MulgaraLexerException;
+import org.mulgara.parser.MulgaraParserException;
+import org.mulgara.query.ConstraintConjunction;
+import org.mulgara.query.ConstraintDisjunction;
+import org.mulgara.query.ConstraintExpression;
+import org.mulgara.query.ConstraintIs;
+import org.mulgara.query.ModelExpression;
+import org.mulgara.query.ModelResource;
+import org.mulgara.query.ModelUnion;
+import org.mulgara.query.Order;
+import org.mulgara.query.Query;
+import org.mulgara.query.SelectElement;
+import org.mulgara.query.UnconstrainedAnswer;
+import org.mulgara.query.Variable;
+import org.mulgara.query.operation.Command;
+import org.mulgara.query.rdf.URIReferenceImpl;
+import org.mulgara.sparql.parser.ParseException;
+import org.mulgara.sparql.parser.QueryStructure;
+import org.mulgara.sparql.parser.SparqlParser;
+import org.mulgara.sparql.parser.cst.Expression;
+import org.mulgara.sparql.parser.cst.GroupGraphPattern;
+import org.mulgara.sparql.parser.cst.IRIReference;
+import org.mulgara.sparql.parser.cst.Node;
+import org.mulgara.sparql.parser.cst.Ordering;
+
+
+/**
+ * Converts a parsed SPARQL query into a Command for execution.
+ *
+ * @created Apr 18, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class SparqlInterpreter implements Interpreter {
+
+  /** The default graph to use if none has been set. */
+  private static final URI INTERNAL_DEFAULT_GRAPH_URI = URI.create("local:null");
+
+  /** The default graph to use when none has been parsed. */
+  private URI defaultGraphUri = null;
+
+  /**
+   * Sets the default graph to use in parsed queries.
+   * @param graph The graph URI to use as the default graph.
+   */
+  public void setDefaultGraphUri(URI graphUri) {
+    defaultGraphUri = graphUri;
+  }
+
+  /**
+   * Gets the default graph to use when none has been parsed from the query.
+   * @return The graph that parsed queries will default to when no FROM graph is supplied.
+   */
+  public URI getDefaultGraphUri() {
+    return (defaultGraphUri != null) ? defaultGraphUri : INTERNAL_DEFAULT_GRAPH_URI;
+  }
+
+  /**
+   * @see org.mulgara.parser.Interpreter#parseCommand(java.lang.String)
+   * The only commands that SPARQL current handles are queries.
+   */
+  public Command parseCommand(String command) throws MulgaraParserException, MulgaraLexerException, IllegalArgumentException, IOException {
+    return parseQuery(command);
+  }
+
+  /**
+   * @see org.mulgara.parser.Interpreter#parseCommands(java.lang.String)
+   * Since SPARQL has no separator character, there can only be one command per string.
+   */
+  public List<Command> parseCommands(String command) throws MulgaraParserException, MulgaraLexerException, IOException, IllegalArgumentException {
+    return Collections.singletonList(parseCommand(command));
+  }
+
+  /**
+   * @see org.mulgara.parser.Interpreter#parseQuery(java.lang.String)
+   */
+  public Query parseQuery(String queryString) throws IOException, MulgaraLexerException, MulgaraParserException {
+    QueryStructure struct;
+    try {
+      struct = SparqlParser.parse(queryString);
+    } catch (ParseException pe) {
+      throw new MulgaraParserException(pe);
+    }
+    switch (struct.getType()) {
+      case select:
+        return buildSelectQuery(struct);
+      case construct:
+        return unhandledType(struct);
+      case describe:
+        return unhandledType(struct);
+      case ask:
+        return unhandledType(struct);
+      default:
+        throw new MulgaraParserException("Unknown query type: " + struct.getType().name());
+    }
+  }
+
+  /**
+   * Respond to an unhandled query type.
+   * @param struct The structure representing the query
+   * @return Nothing. An exception is always thrown.
+   * @throws UnsupportedOperationException An exception explaining that this query type is not handled.
+   */
+  private Query unhandledType(QueryStructure struct) throws UnsupportedOperationException {
+    throw new UnsupportedOperationException("Query type not yet supported: " + struct.getType().name());
+  }
+
+  /**
+   * Converts the elements of a {@link QueryStructure} into a Mulgara {@link Query}.
+   * @param queryStruct The structure to analyze and convert.
+   * @return A new query that can be run as a {@link org.mulgara.query.operation.Command} on a connection.
+   * @throws MulgaraParserException If the query structure contains elements that are not supported by Mulgara.
+   */
+  Query buildSelectQuery(QueryStructure queryStruct) throws MulgaraParserException {
+    List<? extends SelectElement> selection = getSelection(queryStruct);
+    ModelExpression defaultGraphs = getFrom(queryStruct);
+    ConstraintExpression whereClause = getWhere(queryStruct);
+    List<Order> orderBy = getOrdering(queryStruct);
+    Integer limit = getLimit(queryStruct);
+    int offset = queryStruct.getOffset();
+    // null having, unconstrained answer
+    return new Query(selection, defaultGraphs, whereClause, null, orderBy, limit, offset, new UnconstrainedAnswer());
+  }
+
+  /**
+   * Extract the requested variables from this query into a list.
+   * @param queryStruct The query to get the selected variables from.
+   * @return A new list containing Mulgara {@link Variable}s.
+   * @throws MulgaraParserException If and selected elements are not variables.
+   */
+  List<? extends SelectElement> getSelection(QueryStructure queryStruct) throws MulgaraParserException {
+    List<Variable> result = new ArrayList<Variable>();
+    if (queryStruct.isSelectAll()) {
+      Collection<org.mulgara.sparql.parser.cst.Variable> allVars = queryStruct.getAllVariables();
+      for (org.mulgara.sparql.parser.cst.Variable v: allVars) result.add(new Variable(v.getName()));
+    } else {
+      List<? extends Node> selection = queryStruct.getSelection();
+      for (Node n: selection) {
+        // SPARQL only permits variables
+        if (!(n instanceof org.mulgara.sparql.parser.cst.Variable)) throw new MulgaraParserException("Unexpected non-variable in the SELECT clause");
+        org.mulgara.sparql.parser.cst.Variable cv = (org.mulgara.sparql.parser.cst.Variable)n;
+        result.add(new Variable(cv.getName()));
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Gets the graph expression ({@link ModelExpression}) the represents the FROM clause, or the default
+   * graph if none was provided.
+   * @param queryStruct The structure to query for the FROM clause.
+   * @return A ModelExpression containing all the required graphs as a union. TODO: this should be a merge.
+   */
+  ModelExpression getFrom(QueryStructure queryStruct) {
+    List<IRIReference> iris = queryStruct.getDefaultFroms();
+    // if there were no named graphs, then use the default
+    if (iris.isEmpty()) return new ModelResource(getDefaultGraphUri());
+    // accumulate the remaining graphs as a union
+    return graphUnion(iris);
+  }
+
+  /**
+   * Convert a list of IRIs into a model resource union of minimal depth. This recurses through construction
+   * of a tree of binary unions, rather than creating a linear linked list of unions.
+   * @param iris The list to convert.
+   * @return A ModelExpression which is a union of all the elements in the list,
+   *   or a {@link ModelResource} if the list contains only one element.
+   */
+  private ModelExpression graphUnion(List<IRIReference> iris) {
+    int listSize = iris.size();
+    // terminate on singleton lists
+    if (listSize == 1) return new ModelResource(iris.get(0).getUri());
+    // short circuit for 2 element lists - optimization
+    if (listSize == 2) return new ModelUnion(new ModelResource(iris.get(0).getUri()), new ModelResource(iris.get(1).getUri()));
+    // general case
+    return new ModelUnion(graphUnion(iris.subList(0, listSize / 2)), graphUnion(iris.subList(listSize / 2, listSize)));
+  }
+
+  /**
+   * Creates a list of the ordering to apply to the results. While SPARQL permits ordering by complex
+   * expressions, this is not supported.
+   * @param queryStruct The query structure.
+   * @return A list of {@link Order}, which are each ordered ascending or descending by variable.
+   * @throws MulgaraParserException If the ORDER BY expression was more complex than a simple variable.
+   */
+  List<Order> getOrdering(QueryStructure queryStruct) throws MulgaraParserException {
+    List<Ordering> orderings = queryStruct.getOrderings();
+    List<Order> result = new ArrayList<Order>(orderings.size());
+    for (Ordering order: orderings) {
+      Expression v = order.getExpr();
+      if (!(v instanceof org.mulgara.sparql.parser.cst.Variable)) throw new MulgaraParserException("Unable to support arbitrarily complex ORDER BY clauses.");
+      org.mulgara.sparql.parser.cst.Variable var = (org.mulgara.sparql.parser.cst.Variable)v;
+      result.add(new Order(new Variable(var.getName()), order.isAscending()));
+    }
+    return result;
+  }
+
+  /**
+   * Get the limit described by the query.
+   * @param queryStruct The structure of the query.
+   * @return A {@link java.lang.Integer} containing the limit, or <code>null</code> if there is no limit.
+   */
+  Integer getLimit(QueryStructure queryStruct) {
+    int limit = queryStruct.getLimit();
+    return limit == -1 ? null : limit;
+  }
+
+  /**
+   * Build a WHERE clause for a Mulgara query out of a SPARQL WHERE clause.
+   * @param queryStruct The SPARQL query structure to analyze for the WHERE clause.
+   * @return A Mulgara WHERE clause, as a {@link ConstraintExpression}.
+   */
+  ConstraintExpression getWhere(QueryStructure queryStruct) {
+    // get the basic pattern
+    GroupGraphPattern pattern = queryStruct.getWhereClause();
+    PatternMapper patternMapper = new PatternMapper(pattern);
+    ConstraintExpression result = patternMapper.mapToConstraints();
+    // apply the FROM NAMED expression
+    // TODO: This needs to become a Constraint that wraps LiteralTuples.
+    List<IRIReference> namedFroms = queryStruct.getNamedFroms();
+    if (!namedFroms.isEmpty()) result = addNamedFroms(result, namedFroms, patternMapper.getGraphVars());
+    // possible to ask for non-variables that were employed in GRAPH statements as a parser check.
+    return result;
+  }
+
+  /**
+   * Add in the FROM NAMED values to provide a binding list for each variable used in GRAPH statements.
+   * @param expr The total expression to be modified. This is the WHERE clause.
+   * @param graphs The list of graphs given in the FROM NAMED clauses.
+   * @param graphVars The variables that are used in GRAPH statements.
+   * @return A modified form of expr, with all the graph variables pre-bound.
+   */
+  ConstraintExpression addNamedFroms(ConstraintExpression expr, List<IRIReference> graphs, Set<Variable> graphVars) {
+    List<ConstraintExpression> params = new ArrayList<ConstraintExpression>(graphVars.size() + 1);
+    params.add(expr);
+    for (Variable var: graphVars) params.add(newListConstraint(var, graphs));
+    return new ConstraintConjunction(params);
+  }
+
+  /**
+   * Construct a constraint expression that binds a variable to a list of values.
+   * TODO: This needs to be represented by a new Constraint that gets converted to a LiteralTuples.
+   * @param var The variable to bind.
+   * @param bindingList The list of values to bind the variable to.
+   * @return A new {@link org.mulgara.query.Constraint} that represents the variable binding.
+   */
+  ConstraintExpression newListConstraint(Variable var, List<IRIReference> bindingList) {
+    List<ConstraintExpression> isConstraints = new ArrayList<ConstraintExpression>(bindingList.size());
+    for (IRIReference iri: bindingList) {
+      // does this need a graph node that isn't variable?
+      isConstraints.add(new ConstraintIs(var, new URIReferenceImpl(iri.getUri())));
+    }
+    return new ConstraintDisjunction(isConstraints);
+  }
+}




More information about the Mulgara-svn mailing list