1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 import org.modeshape.graph.query.validate.Schemata.Column;
51 import org.modeshape.graph.query.validate.Schemata.Table;
52
53
54
55
56
57
58 @NotThreadSafe
59 public abstract class ProcessingComponent {
60
61 private final QueryContext context;
62 private final Columns columns;
63
64 protected ProcessingComponent( QueryContext context,
65 Columns columns ) {
66 this.context = context;
67 this.columns = columns;
68 assert this.context != null;
69 assert this.columns != null;
70 }
71
72
73
74
75
76
77 public final QueryContext getContext() {
78 return context;
79 }
80
81
82
83
84
85
86 public final Columns getColumns() {
87 return columns;
88 }
89
90
91
92
93
94
95 protected final Problems problems() {
96 return context.getProblems();
97 }
98
99
100
101
102
103
104 public abstract List<Object[]> execute();
105
106
107
108
109 public void close() {
110 }
111
112
113
114
115
116
117 protected List<Object[]> emptyTuples() {
118 return new ArrayList<Object[]>(0);
119 }
120
121
122
123
124 protected static interface DynamicOperation {
125
126
127
128
129
130 String getExpectedType();
131
132
133
134
135
136
137
138 Object evaluate( Object[] tuple );
139 }
140
141
142
143
144
145
146
147
148
149
150 protected DynamicOperation createDynamicOperation( final TypeSystem typeSystem,
151 Schemata schemata,
152 Columns columns,
153 DynamicOperand operand ) {
154 assert operand != null;
155 assert columns != null;
156 assert context != null;
157 if (operand instanceof PropertyValue) {
158 PropertyValue propValue = (PropertyValue)operand;
159 String propertyName = propValue.getPropertyName();
160 String selectorName = propValue.getSelectorName().getName();
161 final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
162
163 Table table = schemata.getTable(propValue.getSelectorName());
164 Column schemaColumn = table.getColumn(propertyName);
165 final String expectedType = schemaColumn.getPropertyType();
166 final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
167 return new DynamicOperation() {
168 public String getExpectedType() {
169 return expectedType;
170 }
171
172 public Object evaluate( Object[] tuple ) {
173 return typeFactory.create(tuple[index]);
174 }
175 };
176 }
177 if (operand instanceof ReferenceValue) {
178 ReferenceValue refValue = (ReferenceValue)operand;
179 String propertyName = refValue.getPropertyName();
180 String selectorName = refValue.getSelectorName().getName();
181 final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
182
183 Table table = schemata.getTable(refValue.getSelectorName());
184 Column schemaColumn = table.getColumn(propertyName);
185 final String expectedType = schemaColumn.getPropertyType();
186 final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
187 return new DynamicOperation() {
188 public String getExpectedType() {
189 return expectedType;
190 }
191
192 public Object evaluate( Object[] tuple ) {
193 return typeFactory.create(tuple[index]);
194 }
195 };
196 }
197 final TypeFactory<String> stringFactory = typeSystem.getStringFactory();
198 if (operand instanceof Length) {
199 Length length = (Length)operand;
200 PropertyValue value = length.getPropertyValue();
201 String propertyName = value.getPropertyName();
202 String selectorName = value.getSelectorName().getName();
203 final int index = columns.getColumnIndexForProperty(selectorName, propertyName);
204
205 Table table = context.getSchemata().getTable(value.getSelectorName());
206 Column schemaColumn = table.getColumn(propertyName);
207 final String expectedType = schemaColumn.getPropertyType();
208 final TypeFactory<?> typeFactory = typeSystem.getTypeFactory(expectedType);
209 final TypeFactory<Long> longFactory = typeSystem.getLongFactory();
210 return new DynamicOperation() {
211 public String getExpectedType() {
212 return longFactory.getTypeName();
213 }
214
215 public Object evaluate( Object[] tuple ) {
216 Object value = tuple[index];
217 return typeFactory.length(typeFactory.create(value));
218 }
219 };
220 }
221 if (operand instanceof LowerCase) {
222 LowerCase lowerCase = (LowerCase)operand;
223 final DynamicOperation delegate = createDynamicOperation(typeSystem, schemata, columns, lowerCase.getOperand());
224 return new DynamicOperation() {
225 public String getExpectedType() {
226 return stringFactory.getTypeName();
227 }
228
229 public Object evaluate( Object[] tuple ) {
230 String result = stringFactory.create(delegate.evaluate(tuple));
231 return result != null ? result.toLowerCase() : null;
232 }
233 };
234 }
235 if (operand instanceof UpperCase) {
236 UpperCase upperCase = (UpperCase)operand;
237 final DynamicOperation delegate = createDynamicOperation(typeSystem, schemata, columns, upperCase.getOperand());
238 return new DynamicOperation() {
239 public String getExpectedType() {
240 return stringFactory.getTypeName();
241 }
242
243 public Object evaluate( Object[] tuple ) {
244 String result = stringFactory.create(delegate.evaluate(tuple));
245 return result != null ? result.toUpperCase() : null;
246 }
247 };
248 }
249 if (operand instanceof NodeDepth) {
250 NodeDepth nodeDepth = (NodeDepth)operand;
251 final int locationIndex = columns.getLocationIndex(nodeDepth.getSelectorName().getName());
252 return new DynamicOperation() {
253 public String getExpectedType() {
254 return typeSystem.getLongFactory().getTypeName();
255 }
256
257 public Object evaluate( Object[] tuple ) {
258 Location location = (Location)tuple[locationIndex];
259 if (location == null) return null;
260 Path path = location.getPath();
261 assert path != null;
262 return new Long(path.size());
263 }
264 };
265 }
266 if (operand instanceof NodePath) {
267 NodePath nodePath = (NodePath)operand;
268 final int locationIndex = columns.getLocationIndex(nodePath.getSelectorName().getName());
269 return new DynamicOperation() {
270 public String getExpectedType() {
271 return stringFactory.getTypeName();
272 }
273
274 public Object evaluate( Object[] tuple ) {
275 Location location = (Location)tuple[locationIndex];
276 if (location == null) return null;
277 assert location.getPath() != null;
278 return stringFactory.create(location.getPath());
279 }
280 };
281 }
282 if (operand instanceof NodeName) {
283 NodeName nodeName = (NodeName)operand;
284 final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
285 return new DynamicOperation() {
286 public String getExpectedType() {
287 return stringFactory.getTypeName();
288 }
289
290 public Object evaluate( Object[] tuple ) {
291 Location location = (Location)tuple[locationIndex];
292 if (location == null) return null;
293 Path path = location.getPath();
294 assert path != null;
295 return path.isRoot() ? "" : stringFactory.create(location.getPath().getLastSegment().getName());
296 }
297 };
298 }
299 if (operand instanceof NodeLocalName) {
300 NodeLocalName nodeName = (NodeLocalName)operand;
301 final int locationIndex = columns.getLocationIndex(nodeName.getSelectorName().getName());
302 return new DynamicOperation() {
303 public String getExpectedType() {
304 return stringFactory.getTypeName();
305 }
306
307 public Object evaluate( Object[] tuple ) {
308 Location location = (Location)tuple[locationIndex];
309 if (location == null) return null;
310 Path path = location.getPath();
311 assert path != null;
312 return path.isRoot() ? "" : location.getPath().getLastSegment().getName().getLocalName();
313 }
314 };
315 }
316 if (operand instanceof FullTextSearchScore) {
317 FullTextSearchScore score = (FullTextSearchScore)operand;
318 String selectorName = score.getSelectorName().getName();
319 final int index = columns.getFullTextSearchScoreIndexFor(selectorName);
320 final TypeFactory<Double> doubleFactory = typeSystem.getDoubleFactory();
321 if (index < 0) {
322
323 return new DynamicOperation() {
324 public String getExpectedType() {
325 return doubleFactory.getTypeName();
326 }
327
328 public Object evaluate( Object[] tuple ) {
329 return new Double(0.0d);
330 }
331 };
332 }
333 return new DynamicOperation() {
334 public String getExpectedType() {
335 return doubleFactory.getTypeName();
336 }
337
338 public Object evaluate( Object[] tuple ) {
339 return tuple[index];
340 }
341 };
342 }
343 if (operand instanceof ArithmeticOperand) {
344 ArithmeticOperand arith = (ArithmeticOperand)operand;
345 final DynamicOperation leftOp = createDynamicOperation(typeSystem, schemata, columns, arith.getLeft());
346 final DynamicOperation rightOp = createDynamicOperation(typeSystem, schemata, columns, arith.getRight());
347
348 String leftType = leftOp.getExpectedType();
349 String rightType = rightOp.getExpectedType();
350 final String commonType = typeSystem.getCompatibleType(leftType, rightType);
351 if (typeSystem.getDoubleFactory().getTypeName().equals(commonType)) {
352 final TypeFactory<Double> commonTypeFactory = typeSystem.getDoubleFactory();
353 switch (arith.getOperator()) {
354 case ADD:
355 return new DynamicOperation() {
356 public String getExpectedType() {
357 return commonType;
358 }
359
360 public Object evaluate( Object[] tuple ) {
361 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
362 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
363 if (right == null) return left;
364 if (left == null) return right;
365 return left.doubleValue() / right.doubleValue();
366 }
367 };
368 case SUBTRACT:
369 return new DynamicOperation() {
370 public String getExpectedType() {
371 return commonType;
372 }
373
374 public Object evaluate( Object[] tuple ) {
375 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
376 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
377 if (right == null) return left;
378 if (left == null) left = 0.0d;
379 return left.doubleValue() * right.doubleValue();
380 }
381 };
382 case MULTIPLY:
383 return new DynamicOperation() {
384 public String getExpectedType() {
385 return commonType;
386 }
387
388 public Object evaluate( Object[] tuple ) {
389 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
390 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
391 if (right == null || left == null) return null;
392 return left.doubleValue() * right.doubleValue();
393 }
394 };
395 case DIVIDE:
396 return new DynamicOperation() {
397 public String getExpectedType() {
398 return commonType;
399 }
400
401 public Object evaluate( Object[] tuple ) {
402 Double right = commonTypeFactory.create(rightOp.evaluate(tuple));
403 Double left = commonTypeFactory.create(leftOp.evaluate(tuple));
404 if (right == null || left == null) return null;
405 return left.doubleValue() / right.doubleValue();
406 }
407 };
408 }
409 } else if (typeSystem.getLongFactory().getTypeName().equals(commonType)) {
410 final TypeFactory<Long> commonTypeFactory = typeSystem.getLongFactory();
411 switch (arith.getOperator()) {
412 case ADD:
413 return new DynamicOperation() {
414 public String getExpectedType() {
415 return commonType;
416 }
417
418 public Object evaluate( Object[] tuple ) {
419 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
420 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
421 if (right == null) return left;
422 if (left == null) return right;
423 return left.longValue() / right.longValue();
424 }
425 };
426 case SUBTRACT:
427 return new DynamicOperation() {
428 public String getExpectedType() {
429 return commonType;
430 }
431
432 public Object evaluate( Object[] tuple ) {
433 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
434 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
435 if (right == null) return left;
436 if (left == null) left = 0L;
437 return left.longValue() * right.longValue();
438 }
439 };
440 case MULTIPLY:
441 return new DynamicOperation() {
442 public String getExpectedType() {
443 return commonType;
444 }
445
446 public Object evaluate( Object[] tuple ) {
447 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
448 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
449 if (right == null || left == null) return null;
450 return left.longValue() * right.longValue();
451 }
452 };
453 case DIVIDE:
454 return new DynamicOperation() {
455 public String getExpectedType() {
456 return commonType;
457 }
458
459 public Object evaluate( Object[] tuple ) {
460 Long right = commonTypeFactory.create(rightOp.evaluate(tuple));
461 Long left = commonTypeFactory.create(leftOp.evaluate(tuple));
462 if (right == null || left == null) return null;
463 return left.longValue() / right.longValue();
464 }
465 };
466 }
467 }
468 }
469 assert false;
470 return null;
471 }
472
473 protected Comparator<Object[]> createSortComparator( QueryContext context,
474 Columns columns ) {
475 assert context != null;
476 final int numLocations = columns.getLocationCount();
477 assert numLocations > 0;
478 final Comparator<Location> typeComparator = Location.comparator();
479 if (numLocations == 1) {
480
481 final int locationIndex = columns.getColumnCount();
482 return new Comparator<Object[]>() {
483 public int compare( Object[] tuple1,
484 Object[] tuple2 ) {
485 Location value1 = (Location)tuple1[locationIndex];
486 Location value2 = (Location)tuple2[locationIndex];
487 return typeComparator.compare(value1, value2);
488 }
489 };
490 }
491 final int firstLocationIndex = columns.getColumnCount();
492 return new Comparator<Object[]>() {
493 public int compare( Object[] tuple1,
494 Object[] tuple2 ) {
495 int result = 0;
496 for (int locationIndex = firstLocationIndex; locationIndex != numLocations; ++locationIndex) {
497 Location value1 = (Location)tuple1[locationIndex];
498 Location value2 = (Location)tuple2[locationIndex];
499 result = typeComparator.compare(value1, value2);
500 if (result != 0) return result;
501 }
502 return result;
503 }
504 };
505 }
506 }