View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.query.process;
25  
26  import java.util.ArrayList;
27  import java.util.Comparator;
28  import java.util.List;
29  import net.jcip.annotations.NotThreadSafe;
30  import org.modeshape.common.collection.Problems;
31  import org.modeshape.graph.Location;
32  import org.modeshape.graph.property.Path;
33  import org.modeshape.graph.query.QueryContext;
34  import org.modeshape.graph.query.QueryResults.Columns;
35  import org.modeshape.graph.query.model.ArithmeticOperand;
36  import org.modeshape.graph.query.model.DynamicOperand;
37  import org.modeshape.graph.query.model.FullTextSearchScore;
38  import org.modeshape.graph.query.model.Length;
39  import org.modeshape.graph.query.model.LowerCase;
40  import org.modeshape.graph.query.model.NodeDepth;
41  import org.modeshape.graph.query.model.NodeLocalName;
42  import org.modeshape.graph.query.model.NodeName;
43  import org.modeshape.graph.query.model.NodePath;
44  import org.modeshape.graph.query.model.PropertyValue;
45  import org.modeshape.graph.query.model.ReferenceValue;
46  import org.modeshape.graph.query.model.TypeSystem;
47  import org.modeshape.graph.query.model.UpperCase;
48  import org.modeshape.graph.query.model.TypeSystem.TypeFactory;
49  import org.modeshape.graph.query.validate.Schemata;
50  
51  /**
52   * A component that performs (some) portion of the query processing by {@link #execute() returning the tuples} that result from
53   * this stage of processing. Processing components are designed to be assembled into a processing structure, with a single
54   * component at the top that returns the results of a query.
55   */
56  @NotThreadSafe
57  public abstract class ProcessingComponent {
58  
59      private final QueryContext context;
60      private final Columns columns;
61  
62      protected ProcessingComponent( QueryContext context,
63                                     Columns columns ) {
64          this.context = context;
65          this.columns = columns;
66          assert this.context != null;
67          assert this.columns != null;
68      }
69  
70      /**
71       * Get the context in which this query is being executed.
72       * 
73       * @return context
74       */
75      public final QueryContext getContext() {
76          return context;
77      }
78  
79      /**
80       * Get the column definitions.
81       * 
82       * @return the column mappings; never null
83       */
84      public final Columns getColumns() {
85          return columns;
86      }
87  
88      /**
89       * Get the container for problems encountered during processing.
90       * 
91       * @return the problems container; never null
92       */
93      protected final Problems problems() {
94          return context.getProblems();
95      }
96  
97      /**
98       * Execute this stage of processing and return the resulting tuples that each conform to the {@link #getColumns() columns}.
99       * 
100      * @return the list of tuples, where each tuple corresonds to the {@link #getColumns() columns}; never null
101      */
102     public abstract List<Object[]> execute();
103 
104     /**
105      * Close these results, allowing any resources to be released.
106      */
107     public void close() {
108     }
109 
110     /**
111      * Utility method to create a new tuples list that is empty.
112      * 
113      * @return the empty tuples list; never null
114      */
115     protected List<Object[]> emptyTuples() {
116         return new ArrayList<Object[]>(0);
117     }
118 
119     /**
120      * Interface for evaluating a {@link DynamicOperand} to return the resulting value.
121      */
122     protected static interface DynamicOperation {
123         /**
124          * Get the expected type of the result of this evaluation
125          * 
126          * @return the property type; never null
127          */
128         String getExpectedType();
129 
130         /**
131          * Perform the dynamic evaluation to obtain the desired result.
132          * 
133          * @param tuple the tuple; never null
134          * @return the value that results from dynamically evaluating the operand against the tuple; may be null
135          */
136         Object evaluate( Object[] tuple );
137     }
138 
139     /**
140      * Create a {@link DynamicOperation} instance that is able to evaluate the supplied {@link DynamicOperand}.
141      * 
142      * @param typeSystem the type system; may not be null
143      * @param schemata the schemata; may not be null
144      * @param columns the definition of the result columns and the tuples; may not be null
145      * @param operand the dynamic operand that is to be evaluated by the returned object; may not be null
146      * @return the dynamic operand operation; never null
147      */
148     protected DynamicOperation createDynamicOperation( final TypeSystem typeSystem,
149                                                        Schemata schemata,
150                                                        Columns columns,
151                                                        DynamicOperand operand ) {
152         assert operand != null;
153         assert columns != null;
154         if (operand instanceof PropertyValue) {
155             PropertyValue propValue = (PropertyValue)operand;
156             String propertyName = propValue.propertyName();
157             String selectorName = propValue.selectorName().name();
158             final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
159             // Find the expected property type of the value ...
160             final String expectedType = columns.getColumnTypes().get(index);
161             final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
162             return new DynamicOperation() {
163                 public String getExpectedType() {
164                     return expectedType;
165                 }
166 
167                 public Object evaluate( Object[] tuple ) {
168                     return typeFactory.create(tuple[index]);
169                 }
170             };
171         }
172         if (operand instanceof ReferenceValue) {
173             ReferenceValue refValue = (ReferenceValue)operand;
174             String propertyName = refValue.propertyName();
175             String selectorName = refValue.selectorName().name();
176             final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
177             // Find the expected property type of the value ...
178             final String expectedType = columns.getColumnTypes().get(index);
179             final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
180             return new DynamicOperation() {
181                 public String getExpectedType() {
182                     return expectedType;
183                 }
184 
185                 public Object evaluate( Object[] tuple ) {
186                     return typeFactory.create(tuple[index]);
187                 }
188             };
189         }
190         final TypeFactory<String> stringFactory = typeSystem.getStringFactory();
191         if (operand instanceof Length) {
192             Length length = (Length)operand;
193             PropertyValue value = length.propertyValue();
194             String propertyName = value.propertyName();
195             String selectorName = value.selectorName().name();
196             final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
197             // Find the expected property type of the value ...
198             final String expectedType = columns.getColumnTypes().get(index);
199             final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
200             final TypeFactory<Long> longFactory = typeSystem.getLongFactory();
201             return new DynamicOperation() {
202                 public String getExpectedType() {
203                     return longFactory.getTypeName(); // length is always LONG
204                 }
205 
206                 public Object evaluate( Object[] tuple ) {
207                     Object value = tuple[index];
208                     return typeFactory.length(typeFactory.create(value));
209                 }
210             };
211         }
212         if (operand instanceof LowerCase) {
213             LowerCase lowerCase = (LowerCase)operand;
214             final DynamicOperation delegate = createDynamicOperation(typeSystem, schemata, columns, lowerCase.operand());
215             return new DynamicOperation() {
216                 public String getExpectedType() {
217                     return stringFactory.getTypeName();
218                 }
219 
220                 public Object evaluate( Object[] tuple ) {
221                     String result = stringFactory.create(delegate.evaluate(tuple));
222                     return result != null ? result.toLowerCase() : null;
223                 }
224             };
225         }
226         if (operand instanceof UpperCase) {
227             UpperCase upperCase = (UpperCase)operand;
228             final DynamicOperation delegate = createDynamicOperation(typeSystem, schemata, columns, upperCase.operand());
229             return new DynamicOperation() {
230                 public String getExpectedType() {
231                     return stringFactory.getTypeName();
232                 }
233 
234                 public Object evaluate( Object[] tuple ) {
235                     String result = stringFactory.create(delegate.evaluate(tuple));
236                     return result != null ? result.toUpperCase() : null;
237                 }
238             };
239         }
240         if (operand instanceof NodeDepth) {
241             NodeDepth nodeDepth = (NodeDepth)operand;
242             final int locationIndex = columns.getLocationIndex(nodeDepth.selectorName().name());
243             return new DynamicOperation() {
244                 public String getExpectedType() {
245                     return typeSystem.getLongFactory().getTypeName(); // depth is always LONG
246                 }
247 
248                 public Object evaluate( Object[] tuple ) {
249                     Location location = (Location)tuple[locationIndex];
250                     if (location == null) return null;
251                     Path path = location.getPath();
252                     assert path != null;
253                     return new Long(path.size());
254                 }
255             };
256         }
257         if (operand instanceof NodePath) {
258             NodePath nodePath = (NodePath)operand;
259             final int locationIndex = columns.getLocationIndex(nodePath.selectorName().name());
260             return new DynamicOperation() {
261                 public String getExpectedType() {
262                     return stringFactory.getTypeName();
263                 }
264 
265                 public Object evaluate( Object[] tuple ) {
266                     Location location = (Location)tuple[locationIndex];
267                     if (location == null) return null;
268                     assert location.getPath() != null;
269                     return stringFactory.create(location.getPath());
270                 }
271             };
272         }
273         if (operand instanceof NodeName) {
274             NodeName nodeName = (NodeName)operand;
275             final int locationIndex = columns.getLocationIndex(nodeName.selectorName().name());
276             return new DynamicOperation() {
277                 public String getExpectedType() {
278                     return stringFactory.getTypeName();
279                 }
280 
281                 public Object evaluate( Object[] tuple ) {
282                     Location location = (Location)tuple[locationIndex];
283                     if (location == null) return null;
284                     Path path = location.getPath();
285                     assert path != null;
286                     return path.isRoot() ? "" : stringFactory.create(location.getPath().getLastSegment().getName());
287                 }
288             };
289         }
290         if (operand instanceof NodeLocalName) {
291             NodeLocalName nodeName = (NodeLocalName)operand;
292             final int locationIndex = columns.getLocationIndex(nodeName.selectorName().name());
293             return new DynamicOperation() {
294                 public String getExpectedType() {
295                     return stringFactory.getTypeName();
296                 }
297 
298                 public Object evaluate( Object[] tuple ) {
299                     Location location = (Location)tuple[locationIndex];
300                     if (location == null) return null;
301                     Path path = location.getPath();
302                     assert path != null;
303                     return path.isRoot() ? "" : location.getPath().getLastSegment().getName().getLocalName();
304                 }
305             };
306         }
307         if (operand instanceof FullTextSearchScore) {
308             FullTextSearchScore score = (FullTextSearchScore)operand;
309             String selectorName = score.selectorName().name();
310             final int index = columns.getFullTextSearchScoreIndexFor(selectorName);
311             final TypeFactory<Double> doubleFactory = typeSystem.getDoubleFactory();
312             if (index < 0) {
313                 // No full-text search score for this selector, so return 0.0d;
314                 return new DynamicOperation() {
315                     public String getExpectedType() {
316                         return doubleFactory.getTypeName();
317                     }
318 
319                     public Object evaluate( Object[] tuple ) {
320                         return new Double(0.0d);
321                     }
322                 };
323             }
324             return new DynamicOperation() {
325                 public String getExpectedType() {
326                     return doubleFactory.getTypeName();
327                 }
328 
329                 public Object evaluate( Object[] tuple ) {
330                     return tuple[index];
331                 }
332             };
333         }
334         if (operand instanceof ArithmeticOperand) {
335             ArithmeticOperand arith = (ArithmeticOperand)operand;
336             final DynamicOperation leftOp = createDynamicOperation(typeSystem, schemata, columns, arith.left());
337             final DynamicOperation rightOp = createDynamicOperation(typeSystem, schemata, columns, arith.right());
338             // compute the expected (common) type ...
339             String leftType = leftOp.getExpectedType();
340             String rightType = rightOp.getExpectedType();
341             final String commonType = typeSystem.getCompatibleType(leftType, rightType);
342             if (typeSystem.getDoubleFactory().getTypeName().equals(commonType)) {
343                 final TypeFactory<Double> commonTypeFactory = typeSystem.getDoubleFactory();
344                 switch (arith.operator()) {
345                     case ADD:
346                         return new DynamicOperation() {
347                             public String getExpectedType() {
348                                 return commonType;
349                             }
350 
351                             public Object evaluate( Object[] tuple ) {
352                                 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
353                                 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
354                                 if (right == null) return left;
355                                 if (left == null) return right;
356                                 return left.doubleValue() / right.doubleValue();
357                             }
358                         };
359                     case SUBTRACT:
360                         return new DynamicOperation() {
361                             public String getExpectedType() {
362                                 return commonType;
363                             }
364 
365                             public Object evaluate( Object[] tuple ) {
366                                 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
367                                 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
368                                 if (right == null) return left;
369                                 if (left == null) left = 0.0d;
370                                 return left.doubleValue() * right.doubleValue();
371                             }
372                         };
373                     case MULTIPLY:
374                         return new DynamicOperation() {
375                             public String getExpectedType() {
376                                 return commonType;
377                             }
378 
379                             public Object evaluate( Object[] tuple ) {
380                                 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
381                                 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
382                                 if (right == null || left == null) return null;
383                                 return left.doubleValue() * right.doubleValue();
384                             }
385                         };
386                     case DIVIDE:
387                         return new DynamicOperation() {
388                             public String getExpectedType() {
389                                 return commonType;
390                             }
391 
392                             public Object evaluate( Object[] tuple ) {
393                                 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
394                                 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
395                                 if (right == null || left == null) return null;
396                                 return left.doubleValue() / right.doubleValue();
397                             }
398                         };
399                 }
400             } else if (typeSystem.getLongFactory().getTypeName().equals(commonType)) {
401                 final TypeFactory<Long> commonTypeFactory = typeSystem.getLongFactory();
402                 switch (arith.operator()) {
403                     case ADD:
404                         return new DynamicOperation() {
405                             public String getExpectedType() {
406                                 return commonType;
407                             }
408 
409                             public Object evaluate( Object[] tuple ) {
410                                 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
411                                 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
412                                 if (right == null) return left;
413                                 if (left == null) return right;
414                                 return left.longValue() / right.longValue();
415                             }
416                         };
417                     case SUBTRACT:
418                         return new DynamicOperation() {
419                             public String getExpectedType() {
420                                 return commonType;
421                             }
422 
423                             public Object evaluate( Object[] tuple ) {
424                                 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
425                                 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
426                                 if (right == null) return left;
427                                 if (left == null) left = 0L;
428                                 return left.longValue() * right.longValue();
429                             }
430                         };
431                     case MULTIPLY:
432                         return new DynamicOperation() {
433                             public String getExpectedType() {
434                                 return commonType;
435                             }
436 
437                             public Object evaluate( Object[] tuple ) {
438                                 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
439                                 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
440                                 if (right == null || left == null) return null;
441                                 return left.longValue() * right.longValue();
442                             }
443                         };
444                     case DIVIDE:
445                         return new DynamicOperation() {
446                             public String getExpectedType() {
447                                 return commonType;
448                             }
449 
450                             public Object evaluate( Object[] tuple ) {
451                                 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
452                                 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
453                                 if (right == null || left == null) return null;
454                                 return left.longValue() / right.longValue();
455                             }
456                         };
457                 }
458             }
459         }
460         assert false;
461         return null;
462     }
463 
464     protected Comparator<Object[]> createSortComparator( QueryContext context,
465                                                          Columns columns ) {
466         assert context != null;
467         final int numLocations = columns.getLocationCount();
468         assert numLocations > 0;
469         final Comparator<Location> typeComparator = Location.comparator();
470         if (numLocations == 1) {
471             // We can do this a tad faster if we know there is only one Location object ...
472             final int locationIndex = columns.getColumnCount();
473             return new Comparator<Object[]>() {
474                 public int compare( Object[] tuple1,
475                                     Object[] tuple2 ) {
476                     Location value1 = (Location)tuple1[locationIndex];
477                     Location value2 = (Location)tuple2[locationIndex];
478                     return typeComparator.compare(value1, value2);
479                 }
480             };
481         }
482         final int firstLocationIndex = columns.getColumnCount();
483         return new Comparator<Object[]>() {
484             public int compare( Object[] tuple1,
485                                 Object[] tuple2 ) {
486                 int result = 0;
487                 for (int locationIndex = firstLocationIndex; locationIndex != numLocations; ++locationIndex) {
488                     Location value1 = (Location)tuple1[locationIndex];
489                     Location value2 = (Location)tuple2[locationIndex];
490                     result = typeComparator.compare(value1, value2);
491                     if (result != 0) return result;
492                 }
493                 return result;
494             }
495         };
496     }
497 }