View Javadoc

1   /*
2    * Copyright 2006-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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       * An attempt to convert <b><i>Entity-type</i></b> Specification to Java Persistence Query Language (JPQL) expression.
45       *
46       * @return HQL string, or {@code null} if not applicable
47       */
48      // TODO v0.5: move to Domian specification common representation - parse and build JPQL/HQL out of a structured string instead of this mess...
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          /* This is N/A
55          if (StringUtils.isBlank(tableName)) {
56              throw new IllegalArgumentException("Unable map domain type '" + specification.getType() + "' to table name");
57          }
58          */
59          /* This does not work for tables with discreminators - and this check is not necessary for JPQL either
60          if (!validTableNameList.contains(tableName)) {
61              throw new IllegalArgumentException("Unable map domain type '" + specification.getType() + "' to table name - table named " + tableName + " does not exist");
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      /** Hard-coded table naming convention for now... */
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                  /*if (tableName.equals("level0entityclass")) {
80                      tableName = "Level0EntityClass";
81                  }
82                  return tableName;
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             /* This does not work for tables with discreminators - and this check is not necessary for JPQL either
105             if (!validTableNameList.contains(tableName)) {
106                 throw new IllegalArgumentException("Unable map domain type '" + specification.getType() + "' to table name - table named " + tableName + " does not exist");
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 { // JointDenialSpecification
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                                 //jpqlExpression.append(" = ' '");
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                                         // jpqlExpression.append(" (select count(o) from entity.orders o) > 0
253                                         final Specification<Integer> collectionSpecification = collectionSpecificationComposite.getCollectionSpecification();
254                                         //Specification elementSpecification = collectionSpecificationComposite.getElementSpecification();
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                                         //jpqlExpression.delete(whereClauseIndex + 1, jpqlExpression.length());
263                                         jpqlExpression.append(" where (select count(c) from ");//entity.");
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                             //jpqlExpression.append(HQL_ALL_OBJECTS);
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             // TODO: må ta hensyn til type composite spec her gitt...
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                                 if (leafSpecification instanceof EqualSpecification) {
364                                     ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) leafSpecification;
365                                     final String name;
366                                     if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
367                                         name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
368                                     } else {
369                                         name = parameterizedSpecification.getName();
370                                     }
371                                     Object value = valueBoundSpecification.getValue();
372                                     jpqlExpression.append(" where entity.");
373                                     jpqlExpression.append(name);
374                                     jpqlExpression.append(" ");
375                                     jpqlExpression.append(valueBoundSpecification.getInequality());
376                                     jpqlExpression.append(" ");
377                                     jpqlExpression.append(":");
378                                     jpqlExpression.append(name);
379                                     parameterMap.put(name, value);
380 
381                                 } else if (leafSpecification instanceof GreaterThanSpecification) {
382                                     ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) leafSpecification;
383                                     final String name;
384                                     if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
385                                         name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
386                                     } else {
387                                         name = parameterizedSpecification.getName();
388                                     }
389                                     Object value = valueBoundSpecification.getValue();
390                                     jpqlExpression.append(" where entity.");
391                                     jpqlExpression.append(name);
392                                     jpqlExpression.append(" ");
393                                     jpqlExpression.append(valueBoundSpecification.getInequality());
394                                     jpqlExpression.append(" ");
395                                     jpqlExpression.append(":");
396                                     jpqlExpression.append(name);
397                                     parameterMap.put(name, value);
398 
399                                 } else if (leafSpecification instanceof LessThanSpecification) {
400                                     ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) leafSpecification;
401                                     final String name;
402                                     if (parameterizedSpecification instanceof MethodParameterizedSpecification) {
403                                         name = convertSetterOrGetterMethodName2FieldName(parameterizedSpecification.getName());
404                                     } else {
405                                         name = parameterizedSpecification.getName();
406                                     }
407                                     Object value = valueBoundSpecification.getValue();
408                                     jpqlExpression.append(" where entity.");
409                                     jpqlExpression.append(name);
410                                     jpqlExpression.append(" ");
411                                     jpqlExpression.append(valueBoundSpecification.getInequality());
412                                     jpqlExpression.append(" ");
413                                     jpqlExpression.append(":");
414                                     jpqlExpression.append(name);
415                                     parameterMap.put(name, value);
416 
417                                 } else {
418                                     throw new NotImplementedException();
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                                     //jpqlExpression.append(" <> ' '");
460                                     jpqlExpression.append(" is not null");
461                                 } else {
462                                     //jpqlExpression.append(" = ' '");
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                             if (specificationComposite instanceof GreaterThanSpecification) {
484                                 ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) specificationComposite;
485                                 throw new NotImplementedException();
486 
487                             } else if (specificationComposite instanceof LessThanSpecification) {
488                                 ValueBoundSpecification valueBoundSpecification = (ValueBoundSpecification) specificationComposite;
489                                 jpqlExpression.append(" ");
490                                 jpqlExpression.append(valueBoundSpecification.getInequality(negated));
491                                 jpqlExpression.append(" ");
492                                 String name = getFieldNameFrom(originalParameterizedSpecification);
493                                 jpqlExpression.append(":");
494                                 jpqlExpression.append(name);
495                                 parameterMap.put(name, valueBoundSpecification.getValue());
496 
497                             } else {
498                                 throw new NotImplementedException();
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                                 //jpqlExpression.append(" <> ' '");
513                                 jpqlExpression.append(" is not null");
514                             } else {
515                                 //jpqlExpression.append(" = ' '");
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                                     // jpqlExpression.append(" (select count(o) from entity.orders o) > 0
525                                     Specification<Integer> collectionSpecification = collectionSpecificationComposite.getCollectionSpecification();
526                                     //Specification elementSpecification = collectionSpecificationComposite.getElementSpecification();
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 }