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
51
52
53
54
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
72
73
74
75 public final QueryContext getContext() {
76 return context;
77 }
78
79
80
81
82
83
84 public final Columns getColumns() {
85 return columns;
86 }
87
88
89
90
91
92
93 protected final Problems problems() {
94 return context.getProblems();
95 }
96
97
98
99
100
101
102 public abstract List<Object[]> execute();
103
104
105
106
107 public void close() {
108 }
109
110
111
112
113
114
115 protected List<Object[]> emptyTuples() {
116 return new ArrayList<Object[]>(0);
117 }
118
119
120
121
122 protected static interface DynamicOperation {
123
124
125
126
127
128 String getExpectedType();
129
130
131
132
133
134
135
136 Object evaluate( Object[] tuple );
137 }
138
139
140
141
142
143
144
145
146
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
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
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
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();
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();
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
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
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
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 }