001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.query.parser.serql;
007:
008: import java.util.ArrayList;
009: import java.util.Collection;
010: import java.util.HashMap;
011: import java.util.LinkedHashSet;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Set;
015:
016: import org.openrdf.query.algebra.BNodeGenerator;
017: import org.openrdf.query.algebra.Distinct;
018: import org.openrdf.query.algebra.EmptySet;
019: import org.openrdf.query.algebra.Extension;
020: import org.openrdf.query.algebra.ExtensionElem;
021: import org.openrdf.query.algebra.MultiProjection;
022: import org.openrdf.query.algebra.Projection;
023: import org.openrdf.query.algebra.ProjectionElem;
024: import org.openrdf.query.algebra.ProjectionElemList;
025: import org.openrdf.query.algebra.StatementPattern;
026: import org.openrdf.query.algebra.TupleExpr;
027: import org.openrdf.query.algebra.ValueConstant;
028: import org.openrdf.query.algebra.ValueExpr;
029: import org.openrdf.query.algebra.Var;
030: import org.openrdf.query.algebra.helpers.StatementPatternCollector;
031:
032: class ConstructorBuilder {
033:
034: public TupleExpr buildConstructor(TupleExpr bodyExpr,
035: TupleExpr constructExpr, boolean distinct) {
036: return buildConstructor(bodyExpr, constructExpr, true, distinct);
037: }
038:
039: public TupleExpr buildConstructor(TupleExpr bodyExpr,
040: boolean distinct) {
041: return buildConstructor(bodyExpr, bodyExpr, false, distinct);
042: }
043:
044: private TupleExpr buildConstructor(TupleExpr bodyExpr,
045: TupleExpr constructExpr, boolean explicit, boolean distinct) {
046: TupleExpr result = bodyExpr;
047:
048: // Retrieve all StatementPattern's from the construct expression
049: List<StatementPattern> statementPatterns = StatementPatternCollector
050: .process(constructExpr);
051:
052: Set<Var> constructVars = getConstructVars(statementPatterns);
053:
054: // Note: duplicate elimination is a two-step process. The first step
055: // removes duplicates from the set of constructor variables. After this
056: // step, any bnodes that need to be generated are added to each solution
057: // and these solutions are projected to subject-predicate-object bindings.
058: // Finally, the spo-bindings are again filtered for duplicates.
059: if (distinct) {
060: // Create projection that removes all bindings that are not used in the
061: // constructor
062: ProjectionElemList projElemList = new ProjectionElemList();
063:
064: for (Var var : constructVars) {
065: // Ignore anonymous and constant vars, these will be handled after
066: // the distinct
067: if (!var.isAnonymous() && !var.hasValue()) {
068: projElemList.addElement(new ProjectionElem(var
069: .getName()));
070: }
071: }
072:
073: result = new Projection(result, projElemList);
074:
075: // Filter the duplicates from these projected bindings
076: result = new Distinct(result);
077: }
078:
079: // Create BNodeGenerator's for all anonymous variables
080: Map<Var, ExtensionElem> extElemMap = new HashMap<Var, ExtensionElem>();
081:
082: for (Var var : constructVars) {
083: if (var.isAnonymous() && !extElemMap.containsKey(var)) {
084: ValueExpr valueExpr = null;
085:
086: if (var.hasValue()) {
087: valueExpr = new ValueConstant(var.getValue());
088: } else if (explicit) {
089: // only generate bnodes in case of an explicit constructor
090: valueExpr = new BNodeGenerator();
091: }
092:
093: if (valueExpr != null) {
094: extElemMap.put(var, new ExtensionElem(valueExpr,
095: var.getName()));
096: }
097: }
098: }
099:
100: if (!extElemMap.isEmpty()) {
101: result = new Extension(result, extElemMap.values());
102: }
103:
104: // Create a Projection for each StatementPattern in the constructor
105: List<ProjectionElemList> projections = new ArrayList<ProjectionElemList>();
106:
107: for (StatementPattern sp : statementPatterns) {
108: ProjectionElemList projElemList = new ProjectionElemList();
109:
110: projElemList.addElement(new ProjectionElem(sp
111: .getSubjectVar().getName(), "subject"));
112: projElemList.addElement(new ProjectionElem(sp
113: .getPredicateVar().getName(), "predicate"));
114: projElemList.addElement(new ProjectionElem(sp
115: .getObjectVar().getName(), "object"));
116:
117: projections.add(projElemList);
118: }
119:
120: if (projections.size() == 1) {
121: result = new Projection(result, projections.get(0));
122:
123: // Note: no need to apply the second duplicate elimination step if
124: // there's just one projection
125: } else if (projections.size() > 1) {
126: result = new MultiProjection(result, projections);
127:
128: if (distinct) {
129: // Add another distinct to filter duplicate statements
130: result = new Distinct(result);
131: }
132: } else {
133: // Empty constructor
134: result = new EmptySet();
135: }
136:
137: return result;
138: }
139:
140: /**
141: * Gets the set of variables that are relevant for the constructor. This
142: * method accumulates all subject, predicate and object variables from the
143: * supplied statement patterns, but ignores any context variables.
144: */
145: private Set<Var> getConstructVars(
146: Collection<StatementPattern> statementPatterns) {
147: Set<Var> vars = new LinkedHashSet<Var>(
148: statementPatterns.size() * 2);
149:
150: for (StatementPattern sp : statementPatterns) {
151: vars.add(sp.getSubjectVar());
152: vars.add(sp.getPredicateVar());
153: vars.add(sp.getObjectVar());
154: }
155:
156: return vars;
157: }
158: }
|