1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sourceforge.domian.specification;
17
18
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.lang.NotImplementedException;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import net.sourceforge.domian.entity.Entity;
32 import net.sourceforge.domian.repository.RepositoryException;
33
34 import static java.util.Arrays.asList;
35 import static org.apache.commons.lang.StringUtils.uncapitalize;
36
37
38 public final class Specification2JpqlConverter {
39
40 private static final Logger log = LoggerFactory.getLogger(Specification2JpqlConverter.class);
41
42
43
44
45
46
47
48
49 public static JpqlQueryHolder convertJpaMappedSpecificationType2PreparedJpqlQuery(final CompositeSpecification<? extends Entity> specification,
50 final List<String> validTableNameList) {
51 final Map<String, Object> parameterMap = new HashMap<String, Object>();
52 final StringBuilder jpqlQueryBuilder = new StringBuilder("select entity from ");
53 final String tableName = getTableNameFromDomainType(specification.getType());
54
55
56
57
58
59
60
61
62
63
64 jpqlQueryBuilder.append(tableName);
65 jpqlQueryBuilder.append(" entity");
66 buildJpqlFromCompositeSpecification(specification, jpqlQueryBuilder, parameterMap);
67 return new JpqlQueryHolder(jpqlQueryBuilder.toString(), parameterMap);
68 }
69
70
71 private static String getTableNameFromDomainType(final Class<? extends Entity> domainType) {
72 return domainType.getSimpleName().toUpperCase() + "_TABLE";
73 }
74
75 private static String extractTableNameFromDomainType(final List<String> tableNameList,
76 final Class<? extends Entity> type) {
77 for (String tableName : tableNameList) {
78 if (classNameCorrelates2TableName(tableName, type.getSimpleName())) {
79
80
81
82
83
84 return type.getSimpleName();
85 }
86 }
87 return null;
88 }
89
90 private static boolean classNameCorrelates2TableName(String tableName, String simpleName) {
91 return tableName.toLowerCase().contains(simpleName.toLowerCase());
92 }
93
94
95 public static List<String> convert2DeleteStatement(Specification specification, final List<String> validTableNameList) {
96 final List<String> results = new ArrayList<String>();
97 if (specification.getType().equals(Entity.class)) {
98 results.add("delete from java.lang.Object");
99 return results;
100
101 } else {
102 final StringBuilder jpqlQueryBuilder = new StringBuilder("delete from ");
103 final String tableName = getTableNameFromDomainType(specification.getType());
104
105
106
107
108
109 jpqlQueryBuilder.append(tableName);
110 if (specification instanceof CompositeSpecification) {
111 if (specification instanceof ConjunctionSpecification) {
112 ConjunctionSpecification conjunctionSpecification = (ConjunctionSpecification) specification;
113 for (Object spec : conjunctionSpecification.specifications) {
114 if (spec instanceof ParameterizedSpecification) {
115 ParameterizedSpecification parameterizedSpecification = (ParameterizedSpecification) spec;
116 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
117 MethodParameterizedSpecification methodParameterizedSpecification = (MethodParameterizedSpecification) parameterizedSpecification;
118 Method method = (Method) methodParameterizedSpecification.accessibleObject;
119 String coloumnName = method.getName();
120 coloumnName = convertSetterOrGetterMethodName2FieldName(coloumnName);
121
122 ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) methodParameterizedSpecification.accessibleObjectSpecification;
123 Object value = valueBoundSpecification.getValue();
124
125 jpqlQueryBuilder.append(" entity where");
126 jpqlQueryBuilder.append(" entity.");
127 jpqlQueryBuilder.append(coloumnName);
128 jpqlQueryBuilder.append(" = ");
129 jpqlQueryBuilder.append(value.toString());
130
131 } else if (parameterizedSpecification instanceof FieldParameterizedSpecification) {
132 FieldParameterizedSpecification fieldParameterizedSpecification = (FieldParameterizedSpecification) parameterizedSpecification;
133 Field field = (Field) fieldParameterizedSpecification.accessibleObject;
134 String coloumnName = field.getName();
135
136 ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) fieldParameterizedSpecification.accessibleObjectSpecification;
137 Object value = valueBoundSpecification.getValue();
138
139 jpqlQueryBuilder.append(" entity where");
140 jpqlQueryBuilder.append(" entity.");
141 jpqlQueryBuilder.append(coloumnName);
142 jpqlQueryBuilder.append(" = ");
143 jpqlQueryBuilder.append(value.toString());
144
145 } else {
146 throw new RepositoryException("Unknown parameterized specification type=" + parameterizedSpecification);
147 }
148 }
149 }
150
151 } else if (specification instanceof DisjunctionSpecification) {
152 throw new NotImplementedException();
153
154 } else {
155 throw new NotImplementedException();
156 }
157 }
158 return asList(jpqlQueryBuilder.toString());
159 }
160 }
161
162
163 static String convertSetterOrGetterMethodName2FieldName(String name) {
164 return uncapitalize(name.substring(3));
165 }
166
167
168 private static void buildJpqlFromCompositeSpecification(final CompositeSpecification<? extends Entity> specification,
169 final StringBuilder jpqlExpression,
170 final Map<String, Object> parameterMap) {
171 if (specification instanceof AbstractCompositeSpecification) {
172 AbstractCompositeSpecification abstractCompositeSpecification = (AbstractCompositeSpecification) specification;
173 if (!abstractCompositeSpecification.specifications.isEmpty()) {
174 final Set<Specification<? super Entity>> specificationComposites = abstractCompositeSpecification.specifications;
175 for (Specification<? super Entity> specificationComposite : specificationComposites) {
176 if (specificationComposite instanceof CompositeSpecification) {
177 if (!isTypeOnlyCompositeSpecification((CompositeSpecification<Entity>) specificationComposite)) {
178 buildJpqlFromCompositeSpecification((CompositeSpecification<Entity>) specificationComposite, jpqlExpression, parameterMap);
179 }
180
181 } else if (specificationComposite instanceof ParameterizedSpecification) {
182 ParameterizedSpecification parameterizedSpecification = (ParameterizedSpecification) specificationComposite;
183 if (parameterizedSpecification.accessibleObjectSpecification instanceof CompositeSpecification) {
184 final String name;
185 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
186 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
187 } else {
188 name = parameterizedSpecification.getName();
189 }
190 jpqlExpression.append(" where entity.");
191 jpqlExpression.append(name);
192
193 buildJpqlFromCompositeSpecification((CompositeSpecification<? extends Entity>) parameterizedSpecification.accessibleObjectSpecification,
194 parameterizedSpecification,
195 false,
196 jpqlExpression,
197 parameterMap);
198
199 } else if (parameterizedSpecification.accessibleObjectSpecification instanceof LeafSpecification) {
200 LeafSpecification leafSpecification = (LeafSpecification) parameterizedSpecification.accessibleObjectSpecification;
201 if (leafSpecification instanceof ValueBoundSpecification) {
202 AbstractValueBoundSpecification valueBoundSpecification = (AbstractValueBoundSpecification) leafSpecification;
203 final String name;
204 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
205 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
206 } else {
207 name = parameterizedSpecification.getName();
208 }
209 Object value = valueBoundSpecification.getValue();
210 if (parameterMap.isEmpty()) {
211 jpqlExpression.append(" where ");
212 } else {
213 jpqlExpression.append(" and ");
214 }
215 jpqlExpression.append("entity.");
216 jpqlExpression.append(name);
217 jpqlExpression.append(" ");
218 jpqlExpression.append(valueBoundSpecification.getBinaryRelation().toString());
219 jpqlExpression.append(" ");
220 jpqlExpression.append(":");
221 jpqlExpression.append(name);
222 parameterMap.put(name, value);
223
224 } else if (leafSpecification instanceof NotNullSpecification) {
225 final String name;
226 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
227 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
228 } else {
229 name = parameterizedSpecification.getName();
230 }
231 jpqlExpression.append(" where entity.");
232 jpqlExpression.append(name);
233 jpqlExpression.append(" is not null");
234
235 } else if (leafSpecification instanceof DefaultValueSpecification) {
236 final String name;
237 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
238 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
239 } else {
240 name = parameterizedSpecification.getName();
241 }
242 jpqlExpression.append(" where entity.");
243 jpqlExpression.append(name);
244
245 jpqlExpression.append(" is null");
246
247 } else if (leafSpecification instanceof CollectionSpecification) {
248 final CollectionSpecification collectionSpecificationComposite = (CollectionSpecification) leafSpecification;
249 final CollectionSpecification.CollectionSpecificationScope scope = collectionSpecificationComposite.getCollectionSpecificationScope();
250 switch (scope) {
251 case SIZE:
252
253 final Specification<Integer> collectionSpecification = collectionSpecificationComposite.getCollectionSpecification();
254
255 final int whereClauseIndex = jpqlExpression.lastIndexOf(" ");
256
257 String entityName = jpqlExpression.substring(whereClauseIndex).trim();
258 entityName = entityName.substring(entityName.indexOf('.') + 1, entityName.length());
259
260 String memberName = parameterizedSpecification.getName();
261
262
263 jpqlExpression.append(" where (select count(c) from ");
264 jpqlExpression.append(entityName);
265 jpqlExpression.append(".");
266
267 jpqlExpression.append(memberName);
268 jpqlExpression.append(" c) ");
269 if (collectionSpecification instanceof ValueBoundSpecification) {
270 final AbstractValueBoundSpecification valueBoundCollectionSpecification = (AbstractValueBoundSpecification) collectionSpecification;
271 jpqlExpression.append(valueBoundCollectionSpecification.getBinaryRelation().toString());
272 jpqlExpression.append(" ");
273 jpqlExpression.append(valueBoundCollectionSpecification.getValue());
274 } else {
275 throw new NotImplementedException();
276 }
277 break;
278
279 case NUMBER_OF_APPROVED_ELEMENTS:
280 throw new NotImplementedException();
281
282 case PERCENTAGE_OF_APPROVED_ELEMENTS:
283 throw new NotImplementedException();
284 }
285
286 } else {
287 throw new NotImplementedException();
288 }
289
290 } else {
291 throw new NotImplementedException();
292 }
293
294 } else if (specificationComposite instanceof LeafSpecification) {
295 if (specificationComposite instanceof NotNullSpecification) {
296
297 } else {
298 throw new NotImplementedException();
299 }
300
301 } else {
302 throw new NotImplementedException();
303 }
304 }
305 }
306
307 } else {
308 log.warn("Specification is a " + CompositeSpecification.class.getName() +
309 ", but is not extending " + AbstractCompositeSpecification.class.getName());
310 }
311 }
312
313
314 private static void buildJpqlFromCompositeSpecification(final CompositeSpecification<? extends Entity> compositeSpecification,
315 final ParameterizedSpecification originalParameterizedSpecification,
316 boolean negated,
317 final StringBuilder jpqlExpression,
318 final Map<String, Object> parameterMap) {
319 if (compositeSpecification instanceof AbstractCompositeSpecification) {
320 AbstractCompositeSpecification abstractCompositeSpecification = (AbstractCompositeSpecification) compositeSpecification;
321
322
323 if (compositeSpecification instanceof JointDenialSpecification) {
324 if (abstractCompositeSpecification.specifications.size() < 2) {
325 negated = true;
326 }
327 }
328
329 if (!abstractCompositeSpecification.specifications.isEmpty()) {
330 final Set<Specification<? super Entity>> specificationComposites = abstractCompositeSpecification.specifications;
331 for (Specification<? super Entity> specificationComposite : specificationComposites) {
332
333 if (specificationComposite instanceof CompositeSpecification) {
334 if (!isTypeOnlyCompositeSpecification((CompositeSpecification<Entity>) specificationComposite)) {
335 if (specificationComposite instanceof JointDenialSpecification || negated) {
336 buildJpqlFromCompositeSpecification((CompositeSpecification<Entity>) specificationComposite,
337 originalParameterizedSpecification,
338 true,
339 jpqlExpression,
340 parameterMap);
341 } else {
342 buildJpqlFromCompositeSpecification((CompositeSpecification<Entity>) specificationComposite,
343 originalParameterizedSpecification,
344 false,
345 jpqlExpression,
346 parameterMap);
347 }
348 }
349
350 } else if (specificationComposite instanceof ParameterizedSpecification) {
351 ParameterizedSpecification parameterizedSpecification = (ParameterizedSpecification) specificationComposite;
352 if (parameterizedSpecification.accessibleObjectSpecification instanceof CompositeSpecification) {
353 buildJpqlFromCompositeSpecification((CompositeSpecification<? extends Entity>) parameterizedSpecification.accessibleObjectSpecification,
354 parameterizedSpecification,
355 false,
356 jpqlExpression,
357 parameterMap);
358
359 } else if (parameterizedSpecification.accessibleObjectSpecification instanceof LeafSpecification) {
360 LeafSpecification leafSpecification = (LeafSpecification) parameterizedSpecification.accessibleObjectSpecification;
361 if (leafSpecification instanceof ValueBoundSpecification) {
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 AbstractValueBoundSpecification valueBoundSpecification = (AbstractValueBoundSpecification) leafSpecification;
422 final String name;
423 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
424 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
425 } else {
426 name = parameterizedSpecification.getName();
427 }
428 Object value = valueBoundSpecification.getValue();
429 jpqlExpression.append(" where entity.");
430 jpqlExpression.append(name);
431 jpqlExpression.append(" ");
432 jpqlExpression.append(valueBoundSpecification.getBinaryRelation().toString());
433 jpqlExpression.append(" ");
434 jpqlExpression.append(":");
435 jpqlExpression.append(name);
436 parameterMap.put(name, value);
437
438 } else if (leafSpecification instanceof NotNullSpecification) {
439 final String name;
440 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
441 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
442 } else {
443 name = parameterizedSpecification.getName();
444 }
445 jpqlExpression.append(" where entity.");
446 jpqlExpression.append(name);
447 jpqlExpression.append(" is not null");
448
449 } else if (leafSpecification instanceof DefaultValueSpecification) {
450 final String name;
451 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
452 name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
453 } else {
454 name = parameterizedSpecification.getName();
455 }
456 jpqlExpression.append(" where entity.");
457 jpqlExpression.append(name);
458 if (negated) {
459
460 jpqlExpression.append(" is not null");
461 } else {
462
463 jpqlExpression.append(" is null");
464 }
465 } else {
466 throw new NotImplementedException();
467 }
468
469 } else {
470 throw new NotImplementedException();
471 }
472
473 } else if (specificationComposite instanceof LeafSpecification) {
474 if (specificationComposite instanceof NotNullSpecification) {
475 if (negated) {
476 jpqlExpression.append(" is null");
477 } else {
478 jpqlExpression.append(" is not null");
479 }
480
481 } else if (specificationComposite instanceof ValueBoundSpecification) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501 AbstractValueBoundSpecification valueBoundSpecification = (AbstractValueBoundSpecification) specificationComposite;
502 jpqlExpression.append(" ");
503 jpqlExpression.append(valueBoundSpecification.getBinaryRelation(negated).toString());
504 jpqlExpression.append(" ");
505 String name = getFieldNameFrom(originalParameterizedSpecification);
506 jpqlExpression.append(":");
507 jpqlExpression.append(name);
508 parameterMap.put(name, valueBoundSpecification.getValue());
509
510 } else if (specificationComposite instanceof DefaultValueSpecification) {
511 if (negated) {
512
513 jpqlExpression.append(" is not null");
514 } else {
515
516 jpqlExpression.append(" is null");
517 }
518
519 } else if (specificationComposite instanceof CollectionSpecification) {
520 CollectionSpecification collectionSpecificationComposite = (CollectionSpecification) specificationComposite;
521 CollectionSpecification.CollectionSpecificationScope scope = collectionSpecificationComposite.getCollectionSpecificationScope();
522 switch (scope) {
523 case SIZE:
524
525 Specification<Integer> collectionSpecification = collectionSpecificationComposite.getCollectionSpecification();
526
527 int whereClauseIndex = jpqlExpression.lastIndexOf(" ");
528 String memberName = jpqlExpression.substring(whereClauseIndex).trim();
529 memberName = memberName.substring(memberName.indexOf('.') + 1, memberName.length());
530 jpqlExpression.delete(whereClauseIndex + 1, jpqlExpression.length());
531 jpqlExpression.append("(select count(c) from entity.");
532 jpqlExpression.append(memberName);
533 jpqlExpression.append(" c) ");
534 if (collectionSpecification instanceof ValueBoundSpecification) {
535 AbstractValueBoundSpecification valueBoundCollectionSpecification = (AbstractValueBoundSpecification) collectionSpecification;
536 jpqlExpression.append(valueBoundCollectionSpecification.getBinaryRelation(negated).toString());
537 jpqlExpression.append(" ");
538 jpqlExpression.append(valueBoundCollectionSpecification.getValue());
539 } else {
540 throw new NotImplementedException();
541 }
542 break;
543
544 case NUMBER_OF_APPROVED_ELEMENTS:
545 throw new NotImplementedException();
546
547 case PERCENTAGE_OF_APPROVED_ELEMENTS:
548 throw new NotImplementedException();
549 }
550
551 } else {
552 throw new NotImplementedException();
553 }
554
555 } else {
556 throw new NotImplementedException();
557 }
558 }
559 }
560
561 } else {
562 log.warn("Specification is a " + CompositeSpecification.class.getName() +
563 ", but is not extending " + AbstractCompositeSpecification.class.getName());
564 }
565 }
566
567
568 private static String getFieldNameFrom(final ParameterizedSpecification parameterizedSpecification) {
569 if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
570 return convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
571 } else {
572 return parameterizedSpecification.getName();
573 }
574 }
575
576
577 private static boolean isTypeOnlyCompositeSpecification(final CompositeSpecification<Entity> compositeSpecification) {
578 if (compositeSpecification instanceof AbstractCompositeSpecification) {
579 AbstractCompositeSpecification abstractCompositeSpecification = (AbstractCompositeSpecification) compositeSpecification;
580 return abstractCompositeSpecification.specifications.isEmpty();
581
582 } else {
583 log.warn("Specification is a " + CompositeSpecification.class.getName() +
584 ", but is not extending " + AbstractCompositeSpecification.class.getName());
585 return false;
586 }
587 }
588 }