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.validate;
25
26 import java.util.HashMap;
27 import java.util.Map;
28 import org.modeshape.common.collection.Problems;
29 import org.modeshape.common.i18n.I18n;
30 import org.modeshape.graph.GraphI18n;
31 import org.modeshape.graph.query.QueryContext;
32 import org.modeshape.graph.query.model.AllNodes;
33 import org.modeshape.graph.query.model.ArithmeticOperand;
34 import org.modeshape.graph.query.model.ChildNode;
35 import org.modeshape.graph.query.model.ChildNodeJoinCondition;
36 import org.modeshape.graph.query.model.Column;
37 import org.modeshape.graph.query.model.DescendantNode;
38 import org.modeshape.graph.query.model.DescendantNodeJoinCondition;
39 import org.modeshape.graph.query.model.DynamicOperand;
40 import org.modeshape.graph.query.model.EquiJoinCondition;
41 import org.modeshape.graph.query.model.FullTextSearch;
42 import org.modeshape.graph.query.model.FullTextSearchScore;
43 import org.modeshape.graph.query.model.Length;
44 import org.modeshape.graph.query.model.LowerCase;
45 import org.modeshape.graph.query.model.NamedSelector;
46 import org.modeshape.graph.query.model.NodeDepth;
47 import org.modeshape.graph.query.model.NodeLocalName;
48 import org.modeshape.graph.query.model.NodeName;
49 import org.modeshape.graph.query.model.NodePath;
50 import org.modeshape.graph.query.model.PropertyExistence;
51 import org.modeshape.graph.query.model.PropertyValue;
52 import org.modeshape.graph.query.model.Query;
53 import org.modeshape.graph.query.model.ReferenceValue;
54 import org.modeshape.graph.query.model.SameNode;
55 import org.modeshape.graph.query.model.SameNodeJoinCondition;
56 import org.modeshape.graph.query.model.SelectorName;
57 import org.modeshape.graph.query.model.TypeSystem;
58 import org.modeshape.graph.query.model.Visitor;
59 import org.modeshape.graph.query.model.Visitors.AbstractVisitor;
60 import org.modeshape.graph.query.validate.Schemata.Table;
61
62
63
64
65 public class Validator extends AbstractVisitor {
66
67 private final QueryContext context;
68 private final Problems problems;
69 private final Map<SelectorName, Table> selectorsByNameOrAlias;
70 private final Map<SelectorName, Table> selectorsByName;
71 private final Map<String, Schemata.Column> columnsByAlias;
72 private final boolean validateColumnExistence;
73
74
75
76
77
78 public Validator( QueryContext context,
79 Map<SelectorName, Table> selectorsByName ) {
80 this.context = context;
81 this.problems = this.context.getProblems();
82 this.selectorsByNameOrAlias = selectorsByName;
83 this.selectorsByName = new HashMap<SelectorName, Table>();
84 for (Table table : selectorsByName.values()) {
85 this.selectorsByName.put(table.getName(), table);
86 }
87 this.columnsByAlias = new HashMap<String, Schemata.Column>();
88 this.validateColumnExistence = context.getHints().validateColumnExistance;
89 }
90
91
92
93
94
95
96 @Override
97 public void visit( AllNodes obj ) {
98
99 verifyTable(obj.name());
100 }
101
102
103
104
105
106
107 @Override
108 public void visit( ArithmeticOperand obj ) {
109 verifyArithmeticOperand(obj.left());
110 verifyArithmeticOperand(obj.right());
111 }
112
113 protected void verifyArithmeticOperand( DynamicOperand operand ) {
114
115 if (operand instanceof NodeDepth) {
116
117 } else if (operand instanceof Length) {
118
119 } else if (operand instanceof ArithmeticOperand) {
120
121 } else if (operand instanceof FullTextSearchScore) {
122
123 } else if (operand instanceof PropertyValue) {
124 PropertyValue value = (PropertyValue)operand;
125 SelectorName selector = value.selectorName();
126 String propertyName = value.propertyName();
127 Schemata.Column column = verify(selector, propertyName, this.validateColumnExistence);
128 if (column != null) {
129
130 String columnType = column.getPropertyType();
131 TypeSystem types = context.getTypeSystem();
132 String longType = types.getLongFactory().getTypeName();
133 String doubleType = types.getDoubleFactory().getTypeName();
134 if (longType.equals(types.getCompatibleType(columnType, longType))) {
135
136 } else if (doubleType.equals(types.getCompatibleType(columnType, doubleType))) {
137
138 } else {
139 I18n msg = GraphI18n.columnTypeCannotBeUsedInArithmeticOperation;
140 problems.addError(msg, selector, propertyName, columnType);
141 }
142 }
143 } else {
144 I18n msg = GraphI18n.dynamicOperandCannotBeUsedInArithmeticOperation;
145 problems.addError(msg, operand);
146 }
147 }
148
149
150
151
152
153
154 @Override
155 public void visit( ChildNode obj ) {
156 verify(obj.selectorName());
157 }
158
159
160
161
162
163
164 @Override
165 public void visit( ChildNodeJoinCondition obj ) {
166 verify(obj.parentSelectorName());
167 verify(obj.childSelectorName());
168 }
169
170
171
172
173
174
175 @Override
176 public void visit( Column obj ) {
177 verify(obj.selectorName(), obj.propertyName(), this.validateColumnExistence);
178 }
179
180
181
182
183
184
185 @Override
186 public void visit( DescendantNode obj ) {
187 verify(obj.selectorName());
188 }
189
190
191
192
193
194
195 @Override
196 public void visit( DescendantNodeJoinCondition obj ) {
197 verify(obj.ancestorSelectorName());
198 verify(obj.descendantSelectorName());
199 }
200
201
202
203
204
205
206 @Override
207 public void visit( EquiJoinCondition obj ) {
208 verify(obj.selector1Name(), obj.property1Name(), this.validateColumnExistence);
209 verify(obj.selector2Name(), obj.property2Name(), this.validateColumnExistence);
210 }
211
212
213
214
215
216
217 @Override
218 public void visit( FullTextSearch obj ) {
219 SelectorName selectorName = obj.selectorName();
220 if (obj.propertyName() != null) {
221 Schemata.Column column = verify(selectorName, obj.propertyName(), this.validateColumnExistence);
222 if (column != null) {
223
224 if (!column.isFullTextSearchable()) {
225 problems.addError(GraphI18n.columnIsNotFullTextSearchable, column.getName(), selectorName);
226 }
227 }
228 } else {
229 Table table = verify(selectorName);
230
231 if (table != null && !AllNodes.ALL_NODES_NAME.equals(table.getName())) {
232
233 boolean searchable = false;
234 for (Schemata.Column column : table.getColumns()) {
235 if (column.isFullTextSearchable()) {
236 searchable = true;
237 break;
238 }
239 }
240 if (!searchable) {
241 problems.addError(GraphI18n.tableIsNotFullTextSearchable, selectorName);
242 }
243 }
244 }
245 }
246
247
248
249
250
251
252 @Override
253 public void visit( FullTextSearchScore obj ) {
254 verify(obj.selectorName());
255 }
256
257
258
259
260
261
262 @Override
263 public void visit( Length obj ) {
264 verify(obj.selectorName());
265 }
266
267
268
269
270
271
272 @Override
273 public void visit( LowerCase obj ) {
274 verify(obj.selectorName());
275 }
276
277
278
279
280
281
282 @Override
283 public void visit( NamedSelector obj ) {
284 verify(obj.aliasOrName());
285 }
286
287
288
289
290
291
292 @Override
293 public void visit( NodeDepth obj ) {
294 verify(obj.selectorName());
295 }
296
297
298
299
300
301
302 @Override
303 public void visit( NodeLocalName obj ) {
304 verify(obj.selectorName());
305 }
306
307
308
309
310
311
312 @Override
313 public void visit( NodeName obj ) {
314 verify(obj.selectorName());
315 }
316
317
318
319
320
321
322 @Override
323 public void visit( NodePath obj ) {
324 verify(obj.selectorName());
325 }
326
327
328
329
330
331
332 @Override
333 public void visit( PropertyExistence obj ) {
334 verify(obj.selectorName(), obj.propertyName(), this.validateColumnExistence);
335 }
336
337
338
339
340
341
342 @Override
343 public void visit( PropertyValue obj ) {
344 verify(obj.selectorName(), obj.propertyName(), this.validateColumnExistence);
345 }
346
347
348
349
350
351
352 @Override
353 public void visit( ReferenceValue obj ) {
354 String propName = obj.propertyName();
355 if (propName != null) {
356 verify(obj.selectorName(), propName, this.validateColumnExistence);
357 } else {
358 verify(obj.selectorName());
359 }
360 }
361
362
363
364
365
366
367 @Override
368 public void visit( Query obj ) {
369
370 this.columnsByAlias.clear();
371 for (Column column : obj.columns()) {
372
373 Table table = tableWithNameOrAlias(column.selectorName());
374 if (table != null) {
375 Schemata.Column tableColumn = table.getColumn(column.propertyName());
376 if (tableColumn != null) {
377 this.columnsByAlias.put(column.columnName(), tableColumn);
378 }
379 }
380 }
381 super.visit(obj);
382 }
383
384
385
386
387
388
389 @Override
390 public void visit( SameNode obj ) {
391 verify(obj.selectorName());
392 }
393
394
395
396
397
398
399 @Override
400 public void visit( SameNodeJoinCondition obj ) {
401 verify(obj.selector1Name());
402 verify(obj.selector2Name());
403 }
404
405 protected Table tableWithNameOrAlias( SelectorName tableName ) {
406 Table table = selectorsByNameOrAlias.get(tableName);
407 if (table == null) {
408
409 table = selectorsByName.get(tableName);
410 }
411 return table;
412 }
413
414 protected Table verify( SelectorName selectorName ) {
415 Table table = tableWithNameOrAlias(selectorName);
416 if (table == null) {
417 problems.addError(GraphI18n.tableDoesNotExist, selectorName.name());
418 }
419 return table;
420 }
421
422 protected Table verifyTable( SelectorName tableName ) {
423 Table table = tableWithNameOrAlias(tableName);
424 if (table == null) {
425 problems.addError(GraphI18n.tableDoesNotExist, tableName.name());
426 }
427 return table;
428 }
429
430 protected Schemata.Column verify( SelectorName selectorName,
431 String propertyName,
432 boolean columnIsRequired ) {
433 Table table = tableWithNameOrAlias(selectorName);
434 if (table == null) {
435 problems.addError(GraphI18n.tableDoesNotExist, selectorName.name());
436 return null;
437 }
438 Schemata.Column column = table.getColumn(propertyName);
439 if (column == null) {
440
441 column = this.columnsByAlias.get(propertyName);
442 if (column == null && columnIsRequired) {
443 problems.addError(GraphI18n.columnDoesNotExistOnTable, propertyName, selectorName.name());
444 }
445 }
446 return column;
447 }
448
449 }