1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.caleigo.core.service;
19
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.ListIterator;
26 import java.util.Map;
27
28 import org.caleigo.core.CompositeEntityDescriptor;
29 import org.caleigo.core.DataQuery;
30 import org.caleigo.core.EntityRelationPath;
31 import org.caleigo.core.ICompositeEntityDescriptor;
32 import org.caleigo.core.IEntityDescriptor;
33 import org.caleigo.core.IEntityRelationPath;
34
35
36 /*** Instances of this class is used to get as few uniq aliases as possible
37 * in a SQL SELECT statement's SELECT-, FROM-, JOIN- and WHERE-
38 * expressions/clauses.
39 *
40 * @author Niklas Norberg
41 */
42 public class SQLSelectCommand
43 {
44
45
46 private StringBuffer mSQLCommand = new StringBuffer(2000);
47 private String mRootAlias;
48 private HashMap mUniqAliasMap = new HashMap();
49 private HashMap mDeclaredAliasMap = new HashMap();
50 private EntityRelationPathAliasTree mEntityRelationPathAliasTree = new EntityRelationPathAliasTree();
51
52
53
54 protected String getRootAlias(){
55 return mRootAlias;
56 }
57
58 public String toString(){
59 return mSQLCommand.toString();
60 }
61
62 private String getAvailabeAlias(String suggestedAlias)
63 {
64 for(;;)
65 {
66 if( mUniqAliasMap.containsKey(suggestedAlias) )
67 suggestedAlias = CompositeEntityDescriptor.increaseStringEndingWithNumber(suggestedAlias);
68 else
69 break;
70 }
71 return suggestedAlias;
72 }
73
74 private boolean isAliasTaken(String alias)
75 {
76 if( mUniqAliasMap.containsKey(alias) )
77 return true;
78 else
79 return false;
80 }
81
82 protected boolean isAliasDeclared(String alias)
83 {
84 if( mDeclaredAliasMap.containsKey(alias) )
85 return true;
86 else
87 return false;
88 }
89
90
91 private void printUniqAliases()
92 {
93 Iterator it = mUniqAliasMap.keySet().iterator();
94 while(it.hasNext())
95 System.out.println(it.next());
96 }
97
98 /*** Get uniq aliases for a path.
99 *
100 * @param entityRelationPath a path to get uniq aliases for.
101 * @return a String-array containing uniq aliases for the path.
102 */
103 protected String[] getAliasArray(IEntityRelationPath entityRelationPath)
104 {
105 return mEntityRelationPathAliasTree.getAliasArray(entityRelationPath);
106 }
107
108
109
110 protected void append(String str)
111 {
112 mSQLCommand.append(str);
113 }
114
115 protected void append(char c)
116 {
117 mSQLCommand.append(c);
118 }
119
120 protected void shortenCommandWith(int shortenThisMuch)
121 {
122 mSQLCommand.setLength(mSQLCommand.length()-shortenThisMuch);
123 }
124
125 protected void setRootAlias(String rootAlias){
126 mRootAlias = rootAlias;
127 this.markAliasAsUniq(rootAlias);
128 }
129
130 private void markAliasAsUniq(String uniqAlias)
131 {
132 if( !mUniqAliasMap.containsKey(uniqAlias) )
133 mUniqAliasMap.put(uniqAlias, null);
134 else
135 throw new RuntimeException("The alias: "+uniqAlias+" isn't uniq!");
136 }
137
138 protected void markAliasAsDeclared(String declaredAlias)
139 {
140 if( !mDeclaredAliasMap.containsKey(declaredAlias) )
141 mDeclaredAliasMap.put(declaredAlias, null);
142 else
143 throw new RuntimeException("The alias: "+declaredAlias+
144 "have already been declared once!");
145 }
146
147 /***
148 *
149 * @param entityRelationPathMapForFieldGroups
150 */
151 private void addAndMakeUniqueAliasForPathsInMap(Map entityRelationPathMapForFieldGroups)
152 {
153
154 LinkedList entityRelationPathlist = new LinkedList();
155 Iterator it = entityRelationPathMapForFieldGroups.keySet().iterator();
156
157 if(it.hasNext())
158 entityRelationPathlist.add( (EntityRelationPath)it.next() );
159
160 while(it.hasNext())
161 {
162 IEntityRelationPath entityRelationPath = (IEntityRelationPath)it.next();
163
164 for(int i=0;i<entityRelationPathlist.size();i++)
165 {
166 if( entityRelationPath.getRelationCount() <= ((IEntityRelationPath)entityRelationPathlist.get(i)).getRelationCount() )
167 {
168 entityRelationPathlist.add(i, entityRelationPath);
169 break;
170 }
171 else if ( i == entityRelationPathlist.size()-1 )
172 {
173
174 entityRelationPathlist.add(entityRelationPath);
175 break;
176 }
177 }
178 }
179
180
181
182
183 ListIterator listIter = entityRelationPathlist.listIterator();
184 while(listIter.hasNext())
185 {
186 IEntityRelationPath entityRelationPath = (IEntityRelationPath)listIter.next();
187 String[] aliases = (String[])entityRelationPathMapForFieldGroups.get(entityRelationPath);
188
189
190
191 mEntityRelationPathAliasTree.makeUniqAliasForPath(entityRelationPath, aliases);
192 }
193 }
194
195
196 /*** This method collects all entity relation path in a field group tree.
197 * String-arrays are created and paired with every path.
198 * Lastly every path are added to the internal member
199 * mEntityRelationPathAliasTree and uniq alias are made for slave entities.
200 *
201 * @param rootFieldGroup the root in the field group tree.
202 */
203 protected void collectAllEntityRelationPathsAndMakeUniqAliases(ICompositeEntityDescriptor.ICompositeFieldGroup rootFieldGroup)
204 {
205 HashMap entityRelationPathMapForFieldGroups = new HashMap();
206
207
208 this.collectAllEntityRelationPaths(rootFieldGroup, null, new ArrayList(), entityRelationPathMapForFieldGroups);
209
210 this.addAndMakeUniqueAliasForPathsInMap(entityRelationPathMapForFieldGroups);
211
212
213
214 }
215
216 /*** This method make recursive calls to itself and thereby collects paths
217 * and alias lists in a field group tree.
218 *
219 * @param parent root for a field group tree.
220 * @param entityRelationPath the path to append to (appending is done to a
221 * copy), (should be null for the root).
222 * @param aliasList the list to append aliases to (appending is done to a
223 * copy).
224 * @param entityRelationPathMapForFieldGroups a Map with entity relation
225 * paths as keys and alias as values. The Map is changed by this method.
226 */
227 private void collectAllEntityRelationPaths(final ICompositeEntityDescriptor.ICompositeFieldGroup parent,
228 final EntityRelationPath entityRelationPath,
229 final List aliasList,
230 Map entityRelationPathMapForFieldGroups)
231 {
232 if (entityRelationPath !=null )
233 {
234
235
236 entityRelationPathMapForFieldGroups.put(entityRelationPath, aliasList.toArray(new String[]{}));
237
238
239
240
241
242
243 }
244 for(int j=0; j<parent.getChildFieldGroupCount(); j++)
245 {
246 ICompositeEntityDescriptor.ICompositeFieldGroup child = parent.getChildFieldGroup(j);
247 EntityRelationPath entityRelationPath2Append2;
248
249 ArrayList aliasList2Append2 = new ArrayList(aliasList);
250 if(entityRelationPath ==null)
251 {
252 entityRelationPath2Append2 = new EntityRelationPath(child.getParentRelationPath());
253 aliasList2Append2.add(mRootAlias!=null?mRootAlias:parent.getEntityIdentity());
254 }
255 else
256 {
257
258 entityRelationPath2Append2 = new EntityRelationPath(entityRelationPath);
259 entityRelationPath2Append2.appendRelationPath(new EntityRelationPath(child.getParentRelationPath()));
260 }
261 for (int i = 0; i < child.getParentRelationPath().getRelationCount()-1; i++)
262 aliasList2Append2.add(null);
263
264
265
266
267 if( child.getEntityIdentity() != child.getBaseEntityDescriptor().getCodeName() )
268 {
269 this.markAliasAsUniq(child.getEntityIdentity());
270 aliasList2Append2.add(child.getEntityIdentity());
271
272
273
274 }
275 else
276 aliasList2Append2.add(null);
277
278
279 this.collectAllEntityRelationPaths(child, entityRelationPath2Append2, aliasList2Append2, entityRelationPathMapForFieldGroups);
280 }
281 }
282
283
284
285
286
287 /*** This method collects paths and alias lists in an external qualifer and
288 * make uniq alias.
289 *
290 * @param xTernalQualifiers the external qualifer to collect paths and alias
291 * from.
292 */
293 protected void collectAllEntityRelationPathsAndMakeUniqAliases(DataQuery.ExternalQualifier[] xTernalQualifiers)
294 {
295 HashMap entityRelationPathMapForXternalQualifiers = new HashMap();
296
297
298 this.collectAllEntityRelationPaths(xTernalQualifiers, entityRelationPathMapForXternalQualifiers);
299
300 this.addAndMakeUniqueAliasForPathsInMap(entityRelationPathMapForXternalQualifiers);
301
302
303
304 }
305
306 /*** This method collects paths and alias lists in an external qualifer.
307 *
308 * @param xTernalQualifiers the external qualifer to collect paths and alias
309 * from.
310 */
311 private void collectAllEntityRelationPaths(DataQuery.ExternalQualifier[] xTernalQualifiers, Map entityRelationPathMapForXternalQualifiers)
312 {
313 for (int i = 0; i < xTernalQualifiers.length; i++)
314 {
315 IEntityRelationPath path2Qualifer = xTernalQualifiers[i].getEntityRelationPath();
316 String[] aliases = new String[path2Qualifer.getRelationCount()+1];
317
318
319 IEntityDescriptor firstEntityDescriptorInPath = path2Qualifer.getFirstEntityDescriptor();
320 if(firstEntityDescriptorInPath instanceof ICompositeEntityDescriptor)
321 {
322 Iterator iter = path2Qualifer.getFirstFieldDescriptors();
323
324 firstEntityDescriptorInPath = ((ICompositeEntityDescriptor.ICompositeFieldDescriptor) iter.next()).getFieldGroup().getBaseEntityDescriptor();
325
326 }
327
328 String uniqAlias = SQLSelectCommand.this.getAvailabeAlias(firstEntityDescriptorInPath.getCodeName());
329
330 SQLSelectCommand.this.markAliasAsUniq(uniqAlias);
331
332 aliases[0] = uniqAlias;
333
334
335 aliases[aliases.length-1] = mRootAlias;
336
337
338 entityRelationPathMapForXternalQualifiers.put(path2Qualifer, aliases);
339 }
340 }
341
342
343
344
345
346
347
348
349
350
351 /*** Debug-print method. */
352 private void printAllPathsAndBelongingArray()
353 {
354 Map uniqAliasMap = mEntityRelationPathAliasTree.getUniqAliasMap();
355 mEntityRelationPathAliasTree.printAllPathsAndBelongingArray(uniqAliasMap);
356 }
357
358
359
360 /*** Instances of this class should hold all possibly parts (subpaths) of
361 * all entity relation paths that exist in an IEntityDescriptor-instance and
362 * it's optional belonging DataQuery. EntityRelationPathAliasTree-objects
363 * will represent all paths a tree of uniq aliases. It is used to make a
364 * SQL-select-statement with as few as possible used uniq aliases.
365 *
366 * @author Niklas Norberg
367 */
368 private class EntityRelationPathAliasTree
369 {
370 private HashMap mEntityRelationPathMap_With_Uniq_Aliases = new HashMap();
371
372 private Map getUniqAliasMap()
373 {
374 return mEntityRelationPathMap_With_Uniq_Aliases;
375 }
376
377 /*** Get uniq aliases for a path.
378 *
379 * @param entityRelationPath a path to get uniq aliases for.
380 * @return a String-array containing uniq aliases for the path.
381 */
382 private String[] getAliasArray(IEntityRelationPath entityRelationPath)
383 {
384 return (String[])mEntityRelationPathMap_With_Uniq_Aliases.get(entityRelationPath);
385 }
386
387 /*** Make uniq aliases for a path and ADDS the path and the aliases to the
388 * Map.
389 *
390 * @param path2MakeAliasFor the path to make aliases for.
391 * @param aliasArray2AddAliases2 uniq aliases are added to this array.
392 */
393 private void makeUniqAliasForPath(IEntityRelationPath path2MakeAliasFor, String[] aliasArray2AddAliases2)
394 {
395
396
397 int startCopyAliasFromIndex = 1;
398
399
400 Iterator it = mEntityRelationPathMap_With_Uniq_Aliases.keySet().iterator();
401 while(it.hasNext())
402 {
403 IEntityRelationPath path2CopyAliasFrom = (IEntityRelationPath)it.next();
404 startCopyAliasFromIndex = this.copyAlias( path2MakeAliasFor, aliasArray2AddAliases2, path2CopyAliasFrom, startCopyAliasFromIndex);
405 if (startCopyAliasFromIndex==aliasArray2AddAliases2.length)
406 break;
407 }
408
409
410 if (startCopyAliasFromIndex<aliasArray2AddAliases2.length)
411 {
412
413
414
415 IEntityDescriptor entityDescriptor2MakeAliasFor = path2MakeAliasFor.getFirstEntityDescriptor();
416
417
418 for (int relationIndex = 0; relationIndex < startCopyAliasFromIndex-1; relationIndex++)
419 entityDescriptor2MakeAliasFor = path2MakeAliasFor.getRelation(relationIndex).getRelatedEntityDescriptor(entityDescriptor2MakeAliasFor);
420
421
422 for (int aliasIndex = startCopyAliasFromIndex; aliasIndex < aliasArray2AddAliases2.length; aliasIndex++)
423 {
424 int relationIndex = aliasIndex-1;
425 entityDescriptor2MakeAliasFor = path2MakeAliasFor.getRelation(relationIndex).getRelatedEntityDescriptor(entityDescriptor2MakeAliasFor);
426
427 if(aliasArray2AddAliases2[aliasIndex]!=null)
428 {
429
430 if( !SQLSelectCommand.this.isAliasTaken(aliasArray2AddAliases2[aliasIndex]) )
431 throw new RuntimeException("The alias: "+
432 aliasArray2AddAliases2[aliasIndex]+
433 " should been marked uniq but isn't!");
434 }
435 else
436 {
437
438 String uniqAlias = SQLSelectCommand.this.getAvailabeAlias(entityDescriptor2MakeAliasFor.getCodeName());
439
440 SQLSelectCommand.this.markAliasAsUniq(uniqAlias);
441
442 aliasArray2AddAliases2[aliasIndex] = uniqAlias;
443 }
444 }
445 }
446
447
448 mEntityRelationPathMap_With_Uniq_Aliases.put(path2MakeAliasFor, aliasArray2AddAliases2);
449 }
450
451 /*** Help method that is used to copy uniq alises.
452 * It follows another path as long as the road is same and copy over
453 * aliases. Copying are only made if needed, if aliases are set already
454 * it checks that they are the same in both arrays!
455 *
456 * @param path2MakeAliasFor path to make aliases for.
457 * @param aliasArray2AddAliases2 alias array to copy uniq aliases to.
458 * @param path2CopyAliasFrom path to follow.
459 * @param startingFromIndex index to start copying from.
460 * @return an int representing the index for the next alias to set,
461 * if all aliases are done this integer will equal the length of the
462 * parameter aliasArray2AddAliases2.
463 */
464 private int copyAlias(IEntityRelationPath path2MakeAliasFor,
465 String[] aliasArray2AddAliases2,
466 IEntityRelationPath path2CopyAliasFrom,
467 int startCopyFromIndex)
468 {
469 if(startCopyFromIndex>path2CopyAliasFrom.getRelationCount()+1)
470 {
471
472 return startCopyFromIndex;
473 }
474
475 String[] uniqAliases = this.getAliasArray(path2CopyAliasFrom);
476
477 int pathIndex = 0;
478 int aliasIndex = 1;
479
480 while( aliasIndex < aliasArray2AddAliases2.length)
481 {
482 if(pathIndex==path2CopyAliasFrom.getRelationCount())
483 {
484 break;
485 }
486
487 if( path2MakeAliasFor.getRelation(pathIndex) == path2CopyAliasFrom.getRelation(pathIndex) &&
488 path2MakeAliasFor.isRelationByReference(pathIndex) == path2CopyAliasFrom.isRelationByReference(pathIndex))
489 {
490 if(aliasIndex<startCopyFromIndex || aliasArray2AddAliases2[aliasIndex]!=null)
491 {
492
493 if( !aliasArray2AddAliases2[aliasIndex].equals(uniqAliases[aliasIndex]) )
494 throw new RuntimeException("WHOOPS ALIAS SHOULD BE EQUAL FOR PATH'S FOLLOWING THE SAME PATH!");
495 }
496 else
497 {
498
499 aliasArray2AddAliases2[aliasIndex] = uniqAliases[aliasIndex];
500 }
501
502 pathIndex++;
503 aliasIndex++;
504 continue;
505 }
506 else
507 {
508 break;
509 }
510 }
511
512 return aliasIndex>startCopyFromIndex?aliasIndex:startCopyFromIndex;
513 }
514
515 /*** Debug-method that prints all IEntityRelationPaths and the beloning
516 * String-array that are contained in the parameter entityRelationPathMap
517 */
518 private void printAllPathsAndBelongingArray(Map entityRelationPathMap)
519 {
520 Iterator it = entityRelationPathMap.keySet().iterator();
521 if (it.hasNext())
522 {
523 System.out.println("\n\n");
524 while(it.hasNext())
525 {
526 IEntityRelationPath key = (IEntityRelationPath)it.next();
527 System.out.println(key);
528 String[] aliases = (String[])entityRelationPathMap.get(key);
529 int i = 0;
530 while (i < aliases.length-1)
531 System.out.print(aliases[i++]+", ");
532 System.out.println(aliases[i]);
533 }
534 System.out.println("\n\n");
535 }
536 }
537
538 }
539
540
541
542 }