1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.caleigo.core;
20
21 import java.util.Map;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.ArrayList;
25 import java.util.Stack;
26 import java.util.Iterator;
27
28 import org.caleigo.core.exception.InvalidDescriptorException;
29 import org.caleigo.core.exception.InvalidFieldException;
30 import org.caleigo.core.exception.InvalidQualifierException;
31 import org.caleigo.core.exception.InvalidRelationException;
32 import org.caleigo.toolkit.log.Log;
33 import org.caleigo.toolkit.util.ResourceProvider;
34
35
36
37
38
39 /*** The CompositeEntityDescriptor can be used to make composite entities based
40 * on fields in base entity descriptors or another composite entity descriptor.
41 *
42 * The entities will be Creatable, Delatable & Editable according to this
43 * scheme:</br>
44 *
45 * An entity is:</br>
46 *
47 * -Deletable if the root group's primary key is present and the root field
48 * group's entity descriptor is deletable.</br>
49 *
50 * -Creatable if the root group's primary key is present and all required fields
51 * in the root group are present and the root group's entity descriptor is
52 * creatable.</br>
53 *
54 * -Editable if any of the field group's primary key is present and there is,
55 * in that group, at least one field that isn't read only.</br>
56 *
57 *
58 * Deleting an entity will delete the root field group's "base entity".</br>
59 *
60 * Creating a new entity will fill the new entity with default values.</br>
61 *
62 * Editing of a field is allowed if the field's composite field descriptor isn't
63 * a read only field (the READ_ONLY_FIELD-flag) and the field group that the
64 * field belongs to has a primary key present and that field group's base entity
65 * descriptor is editable.
66 * Therefor this CompositeEntityDescriptor is editable if any of it's field is
67 * editable.
68 * Upon saving each field group is saved separately, this is done by copying the
69 * data to new entities that has the field group's underlying base entity
70 * descriptor as entity descriptor.
71 *
72 * @author Dennis Zikovic
73 * @author Niklas Norberg
74 * @version 1.00
75 *
76 *//*
77 *
78 * WHEN WHO WHY & WHAT
79 * ------------------------------------------------------------------------------
80 * 2001-07-17 Dennis Zikovic Creation
81 * 2003-11-01 Niklas Norberg Implementation
82 * 2004-07-09 nno, dzi Released
83 */
84 public final class CompositeEntityDescriptor extends AbstractEntityDescriptor implements ICompositeEntityDescriptor
85 {
86
87 private CompositeFieldGroup mRootFieldGroup;
88 private Qualifier mDefaultQualifier = null;
89
90
91
92 /*** This constructor make joins with an entity descriptor and fields.
93 * @param displayName screen name.
94 * @param dataSourceClassName the class name of the underlying data source
95 * that this composite entity descriptor use.
96 * @param entityDescriptor all fields from this will be included if the
97 * parameter joinedFieldDescriptors doesn't contain fields belonging to this
98 * entity descriptor.
99 * @param joinedFieldDescriptors an array of fields to join with and make
100 * a selection from.
101 */
102 public CompositeEntityDescriptor(String displayName, String dataSourceClassName, IEntityDescriptor entityDescriptor, IFieldDescriptor[] joinedFieldDescriptors)
103 {
104 this("CUSTOM", displayName, dataSourceClassName, CUSTOM_ENTITY, createRootFieldGroup(entityDescriptor, joinedFieldDescriptors));
105 }
106
107 /*** This constructor creates a CompositeEntityDescriptor from a fieldArray.
108 * Fields can be from different base entity descriptor's, if all belongs to
109 * the same base entity descriptor the result will be a selection! The first
110 * field descriptor's base entity descriptor will be the root group's
111 * base entity descriptor.
112 * @param displayName screen name.
113 * @param dataSourceClassName the class name of the underlying data source
114 * that this composite entity descriptor use.
115 * @param fieldDescriptors fields that are to be joined/selected.
116 */
117 public CompositeEntityDescriptor(String displayName, String dataSourceClassName, IFieldDescriptor[] fieldDescriptors)
118 {
119 this("CUSTOM", displayName, dataSourceClassName, CUSTOM_ENTITY, createRootFieldGroup(fieldDescriptors));
120 }
121
122 /***
123 * @see CompositeEntityDescriptor#CompositeEntityDescriptor(String displayName, String dataSourceClassName, IFieldDescriptor[] fieldDescriptors)
124 * @see AbstractEntityDescriptor#getEntityType() entityType.
125 * @param codeName the code name should be used for programitic
126 * identification.
127 * @param entityType the entity type entities of this descriptor should have.
128 */
129 public CompositeEntityDescriptor(String codeName, String displayName, String dataSourceClassName, int entityType, IFieldDescriptor[] fieldDescriptors)
130 {
131 this(codeName, displayName, dataSourceClassName, entityType, createRootFieldGroup(fieldDescriptors));
132 }
133
134 /*** Called by the builder and other constructors, therefor private.
135 * @param rootFieldGroup the root of the field group tree that this
136 * composite entity descriptor wraps.
137 */
138 private CompositeEntityDescriptor(String codeName, String displayName, String dataSourceClassName, int entityType, CompositeFieldGroup rootFieldGroup)
139 {
140 super(codeName, rootFieldGroup.getBaseEntityDescriptor().getSourceName(), displayName,
141 CustomEntity.class.getName(),
142 dataSourceClassName,
143
144
145 entityType,
146 buildFieldArray(rootFieldGroup),
147 buildRelationArray(rootFieldGroup)
148 );
149
150 mRootFieldGroup = rootFieldGroup;
151 this.setICompositeEntityDescriptor();
152
153
154
155
156
157
158
159
160 int cacheTime = Integer.MAX_VALUE;
161 int sizeFlag = 0;
162
163 for (Iterator iter = mRootFieldGroup.getFieldGroups(); iter.hasNext();)
164 {
165 CompositeFieldGroup group = (CompositeFieldGroup) iter.next();
166 int nextSizeFlag = calculateSizeTypeFlags(group.getBaseEntityDescriptor());
167 if (nextSizeFlag > sizeFlag)
168 sizeFlag = nextSizeFlag;
169 int nextCacheTime = group.getBaseEntityDescriptor().getCacheTime();
170 if (nextCacheTime < cacheTime)
171 cacheTime = nextCacheTime;
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 this.setFlags( sizeFlag );
191
192 this.setCacheTime(cacheTime);
193
194
195 this.setFieldDescriptorFlags();
196
197
198
199 }
200
201
202
203 /*** This method sets the default qualifer for this composite entity
204 * descriptor.
205 *
206 * @param defaultQualifier the qualifer that directly qualifies this
207 * composite entity descriptor
208 */
209 public void setDefaultQualifier(Qualifier defaultQualifier)
210 {
211 if( !defaultQualifier.canDirectlyQualify(this) )
212 throw new InvalidQualifierException("This qualifier: " +
213 "can't directly qualify this composite entity descriptor");
214 else
215 mDefaultQualifier = defaultQualifier;
216 }
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 /*** Constructor help-method */
232 private static CompositeFieldGroup createRootFieldGroup(IFieldDescriptor[] fieldDescriptors)
233 {
234
235 if(fieldDescriptors==null)
236 throw new InvalidDescriptorException("The parameter: \"fieldDescriptors\" mustn't be null!");
237 return createRootFieldGroup(fieldDescriptors[0].getEntityDescriptor(), fieldDescriptors);
238 }
239
240 /*** Constructor help-method */
241 private static CompositeFieldGroup createRootFieldGroup(IEntityDescriptor entityDescriptor, IFieldDescriptor[] joinedFieldDescriptors)
242 {
243 if(entityDescriptor==null)
244 throw new InvalidDescriptorException("The parameter: \"entityDescriptor\" mustn't be null!");
245
246 CompositeFieldGroup newRootGroup;
247 if (entityDescriptor instanceof ICompositeEntityDescriptor)
248 {
249 newRootGroup =
250 new CompositeFieldGroup(((ICompositeEntityDescriptor)entityDescriptor).getRootFieldGroup(),
251 ((ICompositeEntityDescriptor)entityDescriptor).getRootFieldGroup().getRequired(),
252 null, false, null, false, null );
253 }
254 else if (entityDescriptor instanceof IBaseEntityDescriptor)
255 {
256 newRootGroup = createFieldGroup((IBaseEntityDescriptor)entityDescriptor, joinedFieldDescriptors);;
257 }
258 else
259 throw new InvalidDescriptorException("The parameter \"entityDescriptor\" must be an " +
260 "instance of either ICompositeEntityDescriptor OR an IBaseEntityDescriptor.");
261
262
263 addFields2Group( newRootGroup, joinedFieldDescriptors );
264
265 return newRootGroup;
266 }
267
268 /*** Constructor help-method:
269 * Builds a field group with the choosen fields, duplicates and fields from
270 * other base entities are ignored.
271 *
272 * @param baseEntityDescriptor the created field group's
273 * IBaseEntityDescriptor.
274 * @param selectedFieldDescriptors this fields are included in the field
275 * group, if null all fields in the parameter iBaseEntityDescriptor are
276 * included.
277 * @return a new CompositeFieldGroup-object.
278 */
279 private static CompositeFieldGroup createFieldGroup(IBaseEntityDescriptor baseEntityDescriptor, IFieldDescriptor[] selectedFieldDescriptors)
280 {
281 CompositeFieldGroup group = new CompositeFieldGroup(baseEntityDescriptor, baseEntityDescriptor.getCodeName(), null, false);
282
283 boolean noFieldBelongs2TheBaseEntityDescriptor = true;
284 if (selectedFieldDescriptors != null)
285 for (int i = 0; i < selectedFieldDescriptors.length; i++)
286 {
287 if ( baseEntityDescriptor.getFieldIndex(selectedFieldDescriptors[i]) > 0 )
288 noFieldBelongs2TheBaseEntityDescriptor = false;
289 }
290
291 if (noFieldBelongs2TheBaseEntityDescriptor)
292 {
293
294 selectedFieldDescriptors = new IFieldDescriptor[baseEntityDescriptor.getFieldCount()];
295 for(int j=0; j<selectedFieldDescriptors.length; j++)
296 selectedFieldDescriptors[j] = baseEntityDescriptor.getFieldDescriptor(j);
297 }
298
299 Map fieldMap = new HashMap();
300
301 for(int j=0; j<selectedFieldDescriptors.length; j++)
302 {
303
304 if(fieldMap.containsKey(selectedFieldDescriptors[j])
305 || !baseEntityDescriptor.contains(selectedFieldDescriptors[j]))
306 continue;
307
308 IFieldDescriptor field = selectedFieldDescriptors[j];
309 group.addFieldDescriptor(field);
310 fieldMap.put(selectedFieldDescriptors[j], null);
311 }
312
313 return group;
314 }
315
316 /*** Constructor help-method:
317 * Add fields to a field group i.e. make joins.
318 * Fields are added to either an existing field group (if that field's
319 * base entity has a field group i.e.) otherwise a new field group are
320 * created which is added to the root.
321 *
322 * @param rootFieldGroup the root in the join structure.
323 * @param joinedFieldDescriptors fields that are joined.
324 */
325 private static void addFields2Group(CompositeFieldGroup rootFieldGroup, IFieldDescriptor[] joinedFieldDescriptors)
326 {
327 Map groupMap = new HashMap();
328 Map fieldMap = new HashMap();
329
330 ICompositeFieldDescriptor[] fieldsInRootGroupNChildren = buildFieldArray(rootFieldGroup);
331 for (int j=0; j<fieldsInRootGroupNChildren.length; j++)
332 {
333 ICompositeFieldDescriptor iCompositeFieldDescriptor = fieldsInRootGroupNChildren[j];
334 ICompositeFieldGroup group = iCompositeFieldDescriptor.getFieldGroup();
335 groupMap.put( group.getBaseEntityDescriptor(), group);
336 IFieldDescriptor baseField = iCompositeFieldDescriptor.getSourceFieldDescriptor();
337 fieldMap.put(baseField, null);
338 }
339
340
341 if (joinedFieldDescriptors != null)
342 for(int j=0; j<joinedFieldDescriptors.length; j++)
343 {
344 IFieldDescriptor baseField2Join = joinedFieldDescriptors[j];
345 if(baseField2Join instanceof ICompositeFieldDescriptor)
346 baseField2Join = ((ICompositeFieldDescriptor)baseField2Join).getSourceFieldDescriptor();
347
348
349 if( fieldMap.containsKey(baseField2Join) )
350 continue;
351
352
353 CompositeFieldGroup group = (CompositeFieldGroup)groupMap.get(baseField2Join.getEntityDescriptor());
354 if(group==null){
355 IEntityDescriptor baseEntityDescriptor = baseField2Join.getEntityDescriptor();
356 if (baseEntityDescriptor instanceof IBaseEntityDescriptor)
357 {
358 group = new CompositeFieldGroup((IBaseEntityDescriptor)baseEntityDescriptor,
359 baseEntityDescriptor.getCodeName(),
360 EntityRelationPath.create(rootFieldGroup.getBaseEntityDescriptor(), baseEntityDescriptor),
361 false);
362 groupMap.put(baseEntityDescriptor, group);
363 rootFieldGroup.addChildFieldGroup(rootFieldGroup, group, true);
364 }
365 else
366 throw new InvalidDescriptorException("The field must " +
367 "belong to an IBaseEntityDescriptor!");
368 }
369
370 group.addFieldDescriptor(baseField2Join);
371 fieldMap.put(baseField2Join, null);
372 }
373 }
374
375
376
377
378 /*** Builds an array of all fields in the root field group and it's
379 * descendent children.
380 * @param rootFieldGroup the root in the ICompositeEntityDescriptor-object.
381 * @return an array of fields in breadth-first order.
382 */
383 protected static ICompositeFieldDescriptor[] buildFieldArray(ICompositeFieldGroup rootFieldGroup)
384 {
385 return (ICompositeFieldDescriptor[]) buildFieldArray(rootFieldGroup, new ArrayList()).toArray(new ICompositeFieldDescriptor[]{});
386 }
387
388 /*** Help-method, call the tree's nodes recursively. */
389 protected static List buildFieldArray(ICompositeFieldGroup fieldGroup, List fieldList)
390 {
391 if(fieldGroup.getFieldCount()<=0)
392 throw new InvalidFieldException("The field group with " +
393 "entityIdentity == "+fieldGroup.getEntityIdentity() +
394 " must contain fields");
395 for(int j=0; j<fieldGroup.getFieldCount(); j++)
396 fieldList.add(fieldGroup.getFieldDescriptor(j));
397
398 for(int j=0; j<fieldGroup.getChildFieldGroupCount(); j++)
399 fieldList = CompositeEntityDescriptor.buildFieldArray(fieldGroup.getChildFieldGroup(j), fieldList);
400
401 return fieldList;
402 }
403
404
405 /*** Builds all relations that a composite entity desriptor should have.
406 *
407 * @param rootFieldGroup the root of the wrapped field group tree.
408 * @return an entity relation array that contains all possible relations in
409 * a field group tree i.e. all slave-, reference- and id-fields-relation.
410 *
411 * Obs that setting of entity relations on field relations are done in the
412 * createEntityRelation()-method.
413 */
414 protected static IEntityRelation[] buildRelationArray(CompositeFieldGroup rootFieldGroup)
415 {
416
417 ArrayList fieldGroupWithBelongingReferenceRelationsList = new ArrayList();
418 ArrayList fieldGroupWithBelongingTargetRelationsList = new ArrayList();
419 buildRelationList( rootFieldGroup, fieldGroupWithBelongingReferenceRelationsList, fieldGroupWithBelongingTargetRelationsList);
420
421 ArrayList entityRelationList = new ArrayList(fieldGroupWithBelongingReferenceRelationsList.size()+fieldGroupWithBelongingTargetRelationsList.size()+10);
422 int relationCount;
423
424 relationCount = fieldGroupWithBelongingTargetRelationsList.size();
425 entityRelationTargetFieldLabel:
426 for(int i=0;i<relationCount;i++)
427 {
428 CompositeFieldGroup fieldGroup = (CompositeFieldGroup)((Object[])fieldGroupWithBelongingTargetRelationsList.get(i))[0];
429 IEntityRelation iEntityRelations2Copy = (IEntityRelation)((Object[])fieldGroupWithBelongingTargetRelationsList.get(i))[1];
430
431
432 int fieldRelationCount = iEntityRelations2Copy.getFieldCount();
433 IFieldRelation iFieldRelations[] = new IFieldRelation[fieldRelationCount];;
434
435 for(int j=0;j<fieldRelationCount;j++)
436 {
437 IFieldDescriptor referenceField = iEntityRelations2Copy.getFieldRelation(j).getReferenceField();
438 IFieldDescriptor targetField = iEntityRelations2Copy.getFieldRelation(j).getTargetField();
439
440 int index = fieldGroup.getIndexForWrappedField(targetField);
441 if( fieldGroup.getBaseEntityDescriptor().contains(targetField) &&
442 ( index >= 0 ) )
443 {
444
445 targetField = (ICompositeFieldDescriptor)fieldGroup.getFieldDescriptor(index);
446 }
447 else
448 {
449
450
451
452 continue entityRelationTargetFieldLabel;
453 }
454
455 iFieldRelations[j] = new CompositeEntityDescriptor.CompositeFieldRelation(referenceField, targetField);
456 }
457
458 entityRelationList.add(AbstractEntityDescriptor.createEntityRelation(iFieldRelations,
459 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getCodeName(),
460 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getForwardDisplayName(),
461 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getReverseDisplayName()));
462 }
463
464 relationCount = fieldGroupWithBelongingReferenceRelationsList.size();
465 Map referenceFieldMap = new HashMap();
466 entityRelationReferenceFieldLabel:
467 for(int i=0;i<relationCount;i++)
468 {
469 CompositeFieldGroup fieldGroup = (CompositeFieldGroup)((Object[])fieldGroupWithBelongingReferenceRelationsList.get(i))[0];
470 IEntityRelation iEntityRelations2Copy = (IEntityRelation)((Object[])fieldGroupWithBelongingReferenceRelationsList.get(i))[1];
471
472
473 int fieldRelationCount = iEntityRelations2Copy.getFieldCount();
474 IFieldRelation iFieldRelations[] = new IFieldRelation[fieldRelationCount];;
475
476 for(int j=0;j<fieldRelationCount;j++)
477 {
478 IFieldDescriptor referenceField = iEntityRelations2Copy.getFieldRelation(j).getReferenceField();
479 IFieldDescriptor targetField = iEntityRelations2Copy.getFieldRelation(j).getTargetField();
480
481 int index = fieldGroup.getIndexForWrappedField(referenceField);
482 if( fieldGroup.getBaseEntityDescriptor().contains(referenceField) &&
483 ( index >= 0 ) )
484 {
485 referenceField = (ICompositeFieldDescriptor)fieldGroup.getFieldDescriptor(index);
486 referenceFieldMap.put(referenceField, null);
487 }
488 else
489 {
490
491
492
493 continue entityRelationReferenceFieldLabel;
494 }
495
496 iFieldRelations[j] = new CompositeEntityDescriptor.CompositeFieldRelation(referenceField, targetField);
497 }
498
499 entityRelationList.add(AbstractEntityDescriptor.createEntityRelation(iFieldRelations,
500 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getCodeName(),
501 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getForwardDisplayName(),
502 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getReverseDisplayName()));
503 }
504
505
506 buildRelationForIdFieldsList(rootFieldGroup, referenceFieldMap, entityRelationList);
507
508 IEntityRelation[] entityRelation2Return = (IEntityRelation[])entityRelationList.toArray(new IEntityRelation[]{});
509 return entityRelation2Return;
510 }
511
512 /*** Recursive method for collecting relations.
513 *
514 * @param fieldGroup the field group to get relation's from.
515 * @param referenceRelationList the list that holds and get matching field
516 * groups and their belonging reference entity relation's.
517 * @param targetRelationList the list that holds and get matching field
518 * groups and their belonging target entity relation's.
519 */
520 protected static void buildRelationList(final CompositeFieldGroup fieldGroup, List referenceRelationList, List targetRelationList )
521 {
522 for(int j=0; j<fieldGroup.getBaseEntityDescriptor().getEntityRelationCount(); j++)
523 {
524 IEntityRelation iEntityRelation = fieldGroup.getBaseEntityDescriptor().getEntityRelation(j);
525
526
527
528 int fieldRelationCount = iEntityRelation.getFieldCount();
529 ArrayList referenceFieldsInRelationList = new ArrayList(fieldRelationCount);
530 ArrayList targetFieldsInRelationList = new ArrayList(fieldRelationCount);
531 for(int k=0;k<fieldRelationCount;k++)
532 {
533 IFieldDescriptor referenceField = iEntityRelation.getFieldRelation(k).getReferenceField();
534 IFieldDescriptor targetField = iEntityRelation.getFieldRelation(k).getTargetField();
535 if( fieldGroup.getBaseEntityDescriptor().contains(referenceField) )
536 referenceFieldsInRelationList.add(referenceField);
537 if( fieldGroup.getBaseEntityDescriptor().contains(targetField) )
538 targetFieldsInRelationList.add(targetField);
539 }
540
541 IFieldDescriptor[] referenceFieldsInRelation = (IFieldDescriptor[])referenceFieldsInRelationList.toArray(new IFieldDescriptor[]{});
542 IFieldDescriptor[] targetFieldsInRelation = (IFieldDescriptor[])targetFieldsInRelationList.toArray(new IFieldDescriptor[]{});
543
544
545 if ( fieldGroup.determentIfAllFieldsArePresent(referenceFieldsInRelation))
546 {
547 Object[] objects2StoreRelationDataIn = new Object[2];
548 objects2StoreRelationDataIn[0] = fieldGroup;
549 objects2StoreRelationDataIn[1] = iEntityRelation;
550 referenceRelationList.add(objects2StoreRelationDataIn);
551 }
552 if ( fieldGroup.determentIfAllFieldsArePresent(targetFieldsInRelation))
553 {
554 Object[] objects2StoreRelationDataIn = new Object[2];
555 objects2StoreRelationDataIn[0] = fieldGroup;
556 objects2StoreRelationDataIn[1] = iEntityRelation;
557 targetRelationList.add(objects2StoreRelationDataIn);
558 }
559 }
560
561 for(int j=0; j<fieldGroup.getChildFieldGroupCount(); j++)
562 {
563 CompositeFieldGroup child = (CompositeFieldGroup)fieldGroup.getChildFieldGroup(j);
564 CompositeEntityDescriptor.buildRelationList(child, referenceRelationList, targetRelationList);
565 }
566 }
567
568 /*** Method makes recursive call to itself and make reference relation for
569 * id fields from the composite to the field group's base entity descriptor.
570 *
571 * @param fieldGroup field group to look for exactly one id field in.
572 * @param referenceFieldMap map containing composite field descriptors that
573 * already are referencefields.
574 * @param relationList the list to append the found relations to.
575 */
576 protected static void buildRelationForIdFieldsList(final CompositeFieldGroup fieldGroup, final Map referenceFieldMap, List relationList)
577 {
578 makeReferenceRelation:
579 for (int i = 0; i < fieldGroup.getFieldCount(); i++)
580 {
581 ICompositeFieldDescriptor compositeFieldDescriptor = (ICompositeFieldDescriptor)fieldGroup.getFieldDescriptor(i);
582 if( compositeFieldDescriptor.isIdentityField() &&
583 !referenceFieldMap.containsKey(compositeFieldDescriptor) )
584 {
585
586 for (int j = 0; j < fieldGroup.getBaseEntityDescriptor().getFieldCount(); j++)
587 {
588 if( fieldGroup.getBaseEntityDescriptor().getFieldDescriptor(j).isIdentityField() &&
589 fieldGroup.getBaseEntityDescriptor().getFieldDescriptor(j) != compositeFieldDescriptor.getSourceFieldDescriptor() )
590 break makeReferenceRelation;
591 }
592 IFieldDescriptor referenceField = compositeFieldDescriptor;
593 IFieldDescriptor targetField = compositeFieldDescriptor.getSourceFieldDescriptor();
594 IFieldRelation[] fieldRelationArray = new IFieldRelation[]{new CompositeEntityDescriptor.CompositeFieldRelation(referenceField, targetField)};
595
596 relationList.add( AbstractEntityDescriptor.createEntityRelation( fieldRelationArray,
597 fieldGroup.getEntityIdentity()+targetField.getCodeName(),
598 fieldGroup.getEntityIdentity()+referenceField.getCodeName(),
599 fieldGroup.getBaseEntityDescriptor().getCodeName()+targetField.getCodeName() ) );
600 }
601 }
602
603 for(int j=0; j<fieldGroup.getChildFieldGroupCount(); j++)
604 {
605 CompositeFieldGroup child = (CompositeFieldGroup)fieldGroup.getChildFieldGroup(j);
606 CompositeEntityDescriptor.buildRelationForIdFieldsList(child, referenceFieldMap, relationList);
607 }
608 }
609
610
611
612 /*** Set ownership of all field groups, after this is done a mutable call to
613 * any of the mutable private method's will result in an exception!
614 */
615 private void setICompositeEntityDescriptor()
616 {
617 Iterator groupIterator = mRootFieldGroup.getFieldGroups();
618 while (groupIterator.hasNext())
619 {
620 CompositeFieldGroup group = (CompositeFieldGroup)groupIterator.next();
621 group.setICompositeEntityDescriptor(this);
622 }
623 }
624
625
626 /*** Calculate the EDITABLE flag based on:</br>
627 * if any field group's primary key is present and there is, in that group,
628 * at least one field that isn't read only - then consider as editable.
629 * @return an int-flag that either has the value EDITABLE or 0(zero).
630 */
631 private int calculateEditableModeFlag()
632 {
633 Iterator groupIterator = mRootFieldGroup.getFieldGroups();
634 while (groupIterator.hasNext())
635 {
636 CompositeFieldGroup group = (CompositeFieldGroup)groupIterator.next();
637 if ( group.determentIfPrimaryKeyIsPresent() )
638 for (int i=0;i<group.getFieldCount();i++)
639 if( !group.getFieldDescriptor(i).isReadOnly() )
640 return AbstractEntityDescriptor.EDITABLE;
641 }
642 return 0;
643 }
644
645 /*** Calculate the CREATABLE flag based on:</br>
646 * if the root group's primary key is present and all required fields in the
647 * root group are present and the root group's entity descriptor is
648 * creatable - then consider as creatable.
649 * @return an int-flag that either has the value CREATABLE or 0(zero).
650 */
651 private int calculateCreatableModeFlag()
652 {
653
654
655 List list = new ArrayList(mRootFieldGroup.getBaseEntityDescriptor().getFieldCount());
656 for(int i=0;i<mRootFieldGroup.getBaseEntityDescriptor().getFieldCount();i++)
657 {
658 IFieldDescriptor fieldDescriptor = mRootFieldGroup.getBaseEntityDescriptor().getFieldDescriptor(i);
659 if ( fieldDescriptor.isRequired() )
660 {
661 list.add(fieldDescriptor);
662 }
663 }
664 IFieldDescriptor[] requiredFields = (IFieldDescriptor[])list.toArray(new IFieldDescriptor[list.size()]);
665
666 if ( mRootFieldGroup.determentIfPrimaryKeyIsPresent() &&
667 mRootFieldGroup.determentIfAllFieldsArePresent( requiredFields ) &&
668 mRootFieldGroup.getBaseEntityDescriptor().isEditable() )
669 return AbstractEntityDescriptor.CREATABLE;
670 else
671 return 0;
672 }
673
674
675 /*** Calculate the DELATABLE flag based on:
676 * if the root group's primary key is present and the root field group's
677 * entity descriptor is deletable - then consider as deletable..
678 * @return an int-flag that either has the value DELETABLE or 0(zero).
679 */
680 private int calculateDelatableModeFlag()
681 {
682 if ( mRootFieldGroup.determentIfPrimaryKeyIsPresent() &&
683 mRootFieldGroup.getBaseEntityDescriptor().isDeletable() )
684 return AbstractEntityDescriptor.DELETABLE ;
685 else
686 return 0;
687 }
688
689 /*** This method adjust flags on fields. It turns off and set some flags for
690 * all fields not belonging to the root field group.
691 */
692 private void setFieldDescriptorFlags(){
693
694 Iterator iter = this.getRootFieldGroup().getFieldGroups();
695
696
697 if(iter.hasNext())
698 iter.next();
699
700 while(iter.hasNext())
701 {
702 CompositeFieldGroup nextGroup = (CompositeFieldGroup)iter.next();
703 for (int i = 0; i < nextGroup.getFieldCount(); i++)
704 {
705 CompositeFieldGroup.CompositeFieldDescriptor field = (CompositeFieldGroup.CompositeFieldDescriptor)nextGroup.getFieldDescriptor(i);
706
707 int oldFlags = field.mFlags;
708 int newFlags = oldFlags;
709 if (field.isIdentityField())
710 {
711
712
713 newFlags = ( oldFlags & ~ ( IFieldDescriptor.IDENTITY_FIELD | IFieldDescriptor.REQUIRED ) ) | IFieldDescriptor.READ_ONLY_FIELD | IFieldDescriptor.AUTOGEN;
714 }
715 else
716
717 newFlags = oldFlags & ~IFieldDescriptor.REQUIRED;
718 field.mFlags = newFlags;
719
720 }
721
722 }
723 }
724
725 /*** Calculate which flag's are set.
726 * @param entityDescriptor the IEntityDescriptor that are examined.
727 * @return an int representing all flag's that are set.
728 */
729 protected static int calculateEntityDescriptorFlags(IEntityDescriptor entityDescriptor)
730 {
731 int flags = calculateSizeTypeFlags(entityDescriptor);
732
733
734 if(entityDescriptor.isCreatable())
735 flags |= AbstractEntityDescriptor.CREATABLE;
736 if(entityDescriptor.isEditable())
737 flags |= AbstractEntityDescriptor.EDITABLE;
738 if(entityDescriptor.isDeletable())
739 flags |= AbstractEntityDescriptor.DELETABLE;
740
741 return flags;
742 }
743
744 /*** Calculate which of the size type flag's that are set.
745 * @param entityDescriptor the IEntityDescriptor that are examined.
746 * @return an int representing the size type flag that are set.
747 */
748 protected static int calculateSizeTypeFlags(IEntityDescriptor entityDescriptor)
749 {
750 int flags = 0;
751
752 if(entityDescriptor.isSelectable())
753 flags = AbstractEntityDescriptor.SELECTABLE;
754 else if(entityDescriptor.isListable())
755 flags = AbstractEntityDescriptor.LISTABLE;
756 else if(entityDescriptor.isCacheable())
757 flags = AbstractEntityDescriptor.CACHEABLE;
758 else if(entityDescriptor.isScanable())
759 flags = AbstractEntityDescriptor.SCANABLE;
760
761 return flags;
762 }
763
764 /*** @return and integer representing all flags for the field. */
765
766 public static int calculateFieldDescriptorFlags(IFieldDescriptor field)
767 {
768 int flags = 0;
769 if(field.isAutoGenerated())
770 flags |= IFieldDescriptor.AUTOGEN;
771 if(field.isHiddenField())
772 flags |= IFieldDescriptor.HIDDEN_FIELD;
773 if(field.isHintField())
774 flags |= IFieldDescriptor.HINT_FIELD;
775 if(field.isIdentityField())
776 flags |= IFieldDescriptor.IDENTITY_FIELD;
777 if(field.isIndexed())
778 flags |= IFieldDescriptor.INDEXED;
779 if(field.isNameField())
780 flags |= IFieldDescriptor.NAME_FIELD;
781 if(field.isNaturalOrder())
782 flags |= IFieldDescriptor.NATURAL_ORDER;
783 if(field.isOverviewField())
784 flags |= IFieldDescriptor.OVERVIEW_FIELD;
785 if(field.isReadOnly())
786 flags |= IFieldDescriptor.READ_ONLY_FIELD;
787 if(field.isRequired())
788 flags |= IFieldDescriptor.REQUIRED;
789 return flags;
790 }
791
792 /*** Increases (+1) the last number (if any) in a String. If there are
793 * no number "1" will be added to the String i.e. "alias12" -> "alias13" and
794 * "alias" -> "alias1" .
795 * @return a new String with increased number.
796 */
797 public static String increaseStringEndingWithNumber(String anIdentity){
798 int j=anIdentity.length();
799 while (Character.isDigit(anIdentity.charAt(j-1)))
800 j--;
801 int aliasNbr = 1;
802 if (j<anIdentity.length())
803 aliasNbr = Integer.parseInt( anIdentity.substring( j, anIdentity.length() ) ) + 1;
804 return anIdentity.substring(0, j) + aliasNbr;
805 }
806
807
808
809
810
811
812
813 /*** Makes a descriptive log string for the provided entity object.
814 * Will display the data for all field descriptors.
815 */
816 public static String makeLogString(IEntity entity)
817 {
818 if (entity==null)
819 return "entity==null";
820 IEntityDescriptor entityDescriptor = entity.getEntityDescriptor();
821 int fieldCount = entityDescriptor.getFieldCount();
822 IFieldDescriptor[] fields = new IFieldDescriptor[fieldCount];
823 for (int i=0;i<fieldCount;i++)
824 fields[i] = entityDescriptor.getFieldDescriptor(i);
825 return AbstractEntity.makeLogString(entity, fields);
826 }
827
828 /*** Makes a descriptive log string that tells what flags are set on the
829 * provided entity object.
830 *
831 * @param entityDescriptor
832 */
833 public static String printFlags(IEntityDescriptor entityDescriptor)
834 {
835 StringBuffer dbgStringBuffer = new StringBuffer();
836 dbgStringBuffer.append("The entityDescriptor: "+entityDescriptor.getCodeName()+" has the following flags set to:");
837 dbgStringBuffer.append(" isSelectable(): " + entityDescriptor.isSelectable());
838 dbgStringBuffer.append(", isListable(): " + entityDescriptor.isListable());
839 dbgStringBuffer.append(", isCacheable(): " + entityDescriptor.isCacheable());
840 dbgStringBuffer.append(", isScanable(): " + entityDescriptor.isScanable());
841 dbgStringBuffer.append(", isEditable(): " + entityDescriptor.isEditable());
842 dbgStringBuffer.append(", isCreatable(): " + entityDescriptor.isCreatable());
843 dbgStringBuffer.append(", isDeletable(): " + entityDescriptor.isDeletable());
844 dbgStringBuffer.append('.');
845 return dbgStringBuffer.toString();
846 }
847
848
849
850
851
852 /*** Creates an entity of the type described by this descriptor and
853 * loads it with default data.
854 */
855 public IEntity createEntity()
856 {
857
858 IEntity entity = new CustomEntity(this);
859
860
861 for(int j=0; j<this.getFieldCount(); j++)
862 entity.setData(this.getFieldDescriptor(j), this.getFieldDescriptor(j).getDefaultValue());
863
864 return entity;
865 }
866
867 /*** Creates an entity of the type described by this descriptor and
868 * loads it with data from the provided property source.
869 */
870 public IEntity createEntity(IDataProvider propertySource)
871 {
872
873 IEntity entity = new CustomEntity(this);
874
875
876 entity.setData(propertySource);
877
878 return entity;
879 }
880
881 /*** Returns the data source descriptor that this entity is a part of. */
882 public IDataSourceDescriptor getDataSourceDescriptor()
883 {
884 return ((ICompositeFieldDescriptor)mRootFieldGroup.getFieldDescriptor(0)).getSourceFieldDescriptor().getEntityDescriptor().getDataSourceDescriptor();
885 }
886
887 /*** Creates and returns the store action for composite entity objects of the
888 * custom type: composite entity descriptor.
889 *
890 * @return The IEntityAction object that is used to store entity
891 * objects for the called entity.
892 */
893 protected IEntityAction createStoreAction()
894 {
895 return new CompositeStoreEntityAction(this);
896 }
897
898 /*** Creates and returns the delete action for composite entity objects of
899 * the custom type: composite entity descriptor.
900 *
901 * @return The IEntityAction object that is used to delete entity
902 * objects for the called entity.
903 */
904 protected IEntityAction createDeleteAction()
905 {
906 return new CompositeDeleteEntityAction(this);
907 }
908
909 /*** Returns true if entities described by the called descriptor may be
910 * created. If a store operation is called on a non-persistent entity
911 * of the described type an exception should be thrown.
912 */
913 public boolean isCreatable()
914 {
915 return (this.calculateCreatableModeFlag() & AbstractEntityDescriptor.CREATABLE)==AbstractEntityDescriptor.CREATABLE &&
916 !this.getDataSourceDescriptor().isReadOnly();
917 }
918
919 /*** Returns true if entities described by the called descriptor may be
920 * updated. If a store operation is called on a persistent entity
921 * of the described type an exception should be thrown.
922 */
923 public boolean isEditable()
924 {
925 return (this.calculateEditableModeFlag() & AbstractEntityDescriptor.EDITABLE)==AbstractEntityDescriptor.EDITABLE &&
926 !this.getDataSourceDescriptor().isReadOnly();
927 }
928
929 /*** Returns true if entities described by the called descriptor may be
930 * deleted. If a store operation is called on a persistent entity
931 * of the described type an exception should be thrown.
932 */
933 public boolean isDeletable()
934 {
935 return (this.calculateDelatableModeFlag() & AbstractEntityDescriptor.DELETABLE)==AbstractEntityDescriptor.DELETABLE &&
936 !this.getDataSourceDescriptor().isReadOnly();
937 }
938
939
940
941
942
943
944 /*** Returns the root field group for the tree of
945 * field groups that defines this CompositeEntityDescriptor.
946 */
947 public ICompositeFieldGroup getRootFieldGroup()
948 {
949 return mRootFieldGroup;
950 }
951
952 /*** Returns a qualifier that defines the default filter for this entity.
953 * Setting is done in the constructors. If a constructor without a
954 * qualifier-parameter is choosen or the parameter is null, the default
955 * value is null i.e. no discarding of the entities are made.
956 *
957 * It might make sence e.g. to filter out all orders older than a
958 * discontinued date.
959 */
960 public Qualifier getDefaultQualifier()
961 {
962 return mDefaultQualifier;
963 }
964
965
966
967
968
969
970
971
972
973
974 /*** The FieldGroupBuilder is a builder that builds a tree (of fields- and
975 * field-groups) in a top-down manner. But most users shouldn't have to
976 * worry about internal structur and that's why we provide the builder.
977 */
978 public static class Builder
979 {
980
981
982 /*** Stack for fields group's. */
983 private Stack mFieldGroup_Stack;
984
985
986
987 public Builder(IEntityDescriptor entityDescriptor)
988 {
989 this(entityDescriptor, entityDescriptor.getCodeName(), false);
990 }
991
992
993 public Builder(IEntityDescriptor entityDescriptor, String entityIdentity)
994 {
995 this(entityDescriptor, entityIdentity, false);
996 }
997
998 /*** Instantiate a new FieldGroupBuilder and begins the first group and
999 * make it the tree's root. Next step would be to call one of the
1000 * addField()-methods and then maybe one of the beginGroup()-methods.
1001 *
1002 * @param entityDescriptor
1003 * @param entityIdentity id for the root field group.
1004 * @param required determines what kind of joins that are to be made:
1005 * left/right/full outer join / inner join.
1006 * @see ICompositeEntityDescriptor.ICompositeFieldGroup#getRequired()
1007 */
1008 public Builder(IEntityDescriptor entityDescriptor, String entityIdentity, boolean required)
1009 {
1010 mFieldGroup_Stack = new Stack();
1011 mFieldGroup_Stack.add(createCompositeFieldGroup(entityDescriptor, entityIdentity, null, required));
1012 }
1013
1014
1015
1016
1017 public void beginFieldGroup(IEntityDescriptor entityDescriptor)
1018 {
1019 this.beginFieldGroup(entityDescriptor, null, null, false);
1020 }
1021
1022 public void beginFieldGroup(IEntityDescriptor entityDescriptor, String entityIdentity)
1023 {
1024 this.beginFieldGroup(entityDescriptor, entityIdentity, null, false);
1025 }
1026
1027 public void beginFieldGroup(IEntityDescriptor entityDescriptor, IEntityRelationPath parentToChildPath)
1028 {
1029 this.beginFieldGroup(entityDescriptor, null, parentToChildPath, false);
1030 }
1031
1032 public void beginFieldGroup(IEntityDescriptor entityDescriptor, String entityIdentity, boolean required)
1033 {
1034 this.beginFieldGroup(entityDescriptor, entityIdentity, null, required);
1035 }
1036
1037 public void beginFieldGroup(IEntityDescriptor entityDescriptor, String entityIdentity, IEntityRelationPath parentToChildPath)
1038 {
1039 this.beginFieldGroup(entityDescriptor, entityIdentity, parentToChildPath, false);
1040 }
1041
1042 /*** Create a new field group and add it to the current field group as a
1043 * child, i.e. let this newly created field group be the parent of
1044 * future childs i.e. make a new tree branch.
1045 * @see CompositeEntityDescriptor.CompositeFieldGroup#CompositeFieldGroup(IBaseEntityDescriptor iBaseEntityDescriptor, String entityIdentity, IEntityRelationPath parentRelationPath, boolean required)
1046 *
1047 * @param entityDescriptor is the entity descriptor that all added
1048 * fields should be belong to. All added child field group will be
1049 * joined to this one.???????????????????????????????????????????????????????????????????????????????????????????
1050 * @param entityIdentity is the named identity of the created field
1051 * group. It's optional. This will normally be the code name of
1052 * the base entity descriptor unless multiple instances of the
1053 * descriptor exists in the field group structure.
1054 * @param parentRelationPath is the path that connects this field group's
1055 * parent with this field group.
1056 * @param required determines whether or not this field group's fields
1057 * are required or not in joins.
1058 * @see ICompositeEntityDescriptor.ICompositeFieldGroup#getRequired()
1059 */
1060 public void beginFieldGroup(IEntityDescriptor entityDescriptor, String entityIdentity, IEntityRelationPath parentToChildPath, boolean required)
1061 {
1062 CompositeFieldGroup parent = (CompositeFieldGroup)mFieldGroup_Stack.peek();
1063
1064 if (parentToChildPath == null)
1065 {
1066
1067 parentToChildPath = EntityRelationPath.create(parent.getBaseEntityDescriptor(), entityDescriptor);
1068 }
1069 CompositeFieldGroup newFieldGroup = createCompositeFieldGroup(entityDescriptor, entityIdentity, parentToChildPath, required);
1070 boolean resolveEntityIdentityConflicts = false;
1071 if(entityIdentity == null)
1072 resolveEntityIdentityConflicts = true;
1073 parent.addChildFieldGroup((CompositeFieldGroup)mFieldGroup_Stack.elementAt(0), newFieldGroup, resolveEntityIdentityConflicts);
1074 mFieldGroup_Stack.push(newFieldGroup);
1075 }
1076
1077 public void addFieldDescriptor(IFieldDescriptor field)
1078 {
1079 this.addFieldDescriptor(field, field.getCodeName(), field.getDisplayName());
1080 }
1081
1082 public void addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName)
1083 {
1084 CompositeFieldGroup currentFieldGroup = (CompositeFieldGroup)mFieldGroup_Stack.peek();
1085
1086 currentFieldGroup.addFieldDescriptor(field, codeName, displayName);
1087 }
1088
1089 /*** Add a field to the current field group.
1090 * Call to this method is allowed both before and after adding field
1091 * groups (with any of the beginFieldGroup-methods). Compare to files
1092 * and directories in file-systems!
1093 * @see CompositeEntityDescriptor.CompositeFieldGroup#addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1094 */
1095 public void addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1096 {
1097 CompositeFieldGroup currentFieldGroup = (CompositeFieldGroup)mFieldGroup_Stack.peek();
1098 currentFieldGroup.addFieldDescriptor(field, codeName, displayName, flags);
1099 }
1100
1101 /*** Ends the current field group and climp up one level in the tree.
1102 * Throws an InvalidDescriptorException if the current field group
1103 * doesn't contain any fields!
1104 * Extra closings at the top level are ignored.
1105 */
1106 public void endFieldGroup()
1107 {
1108
1109 if(mFieldGroup_Stack.size()<=1)
1110 return;
1111
1112
1113 CompositeFieldGroup currentFieldGroup = (CompositeFieldGroup)mFieldGroup_Stack.pop();
1114
1115
1116 if (currentFieldGroup.getFieldCount() == 0)
1117 throw new InvalidDescriptorException("A field group must contain fields!");
1118 }
1119
1120 /*** @return a created ICompositeEntityDescriptor which wraps a field
1121 * group tree and it's current children (fields and field-groups).
1122 * After calling this method it's ok to add more children and then call
1123 * it again.
1124 * @see CompositeEntityDescriptor#CompositeEntityDescriptor(String codeName, String displayName, String dataSourceClassName, int entityType, IFieldDescriptor[] fieldDescriptors, Qualifier defaultQualifier)
1125 */
1126 public ICompositeEntityDescriptor createCompositeEntityDescriptor(String codeName, String displayName, String dataSourceClassName, int entityType)
1127 {
1128
1129 CompositeFieldGroup rootFieldGroup = (CompositeFieldGroup)mFieldGroup_Stack.elementAt(0);
1130 CompositeFieldGroup newRootFieldGroup = new CompositeFieldGroup(rootFieldGroup, rootFieldGroup.getRequired(), null, false, null, false, null );
1131 return new CompositeEntityDescriptor(codeName, displayName, dataSourceClassName, entityType, newRootFieldGroup);
1132 }
1133
1134
1135
1136
1137
1138 /*** Help method that creates a new CompositeFieldGroup-object were the
1139 * parameter entityDescriptor either can be an instance of
1140 * iCompositeEntityDescriptor or iBaseEntityDescriptor.
1141 */
1142 private static CompositeFieldGroup createCompositeFieldGroup(IEntityDescriptor entityDescriptor, String entityIdentity, IEntityRelationPath parentRelationPath, boolean required)
1143 {
1144 CompositeFieldGroup newFieldGroup;
1145 if (entityDescriptor instanceof IBaseEntityDescriptor)
1146 newFieldGroup = new CompositeFieldGroup((IBaseEntityDescriptor)entityDescriptor, entityIdentity, parentRelationPath, required);
1147 else if (entityDescriptor instanceof ICompositeEntityDescriptor)
1148 {
1149
1150 newFieldGroup = new CompositeFieldGroup(
1151 ((ICompositeEntityDescriptor)entityDescriptor).getRootFieldGroup(),
1152 required, null, false, null, false, null );
1153 newFieldGroup.mEntityIdentity = entityIdentity;
1154 newFieldGroup.mParentRelationPath = parentRelationPath;
1155 }
1156 else
1157 throw new InvalidDescriptorException("Can only handle " +
1158 "IEntityDescriptor that is an instance of either " +
1159 "IBaseEntityDescriptor or ICompositeEntityDescriptor!");
1160
1161 return newFieldGroup;
1162 }
1163
1164 }
1165
1166
1167
1168
1169
1170
1171
1172 /*** The ICompositeFieldGroup is used to group sets of fields belonging to a
1173 * single entity-descriptor (of the class ICompositeEntityDescriptor)
1174 * together and to define their relation with each other.
1175 * Note that several field groups may have the same base entity descriptor
1176 * but represent different records in the data space.
1177 */
1178 public static final class CompositeFieldGroup implements ICompositeFieldGroup
1179 {
1180
1181
1182 /*** The \"base entity descriptor\" for this field group. */
1183 private IBaseEntityDescriptor mIBaseEntityDescriptor;
1184
1185 /*** Used in SQL as alias. */
1186 private String mEntityIdentity;
1187
1188 /*** The IEntityRelationPath that make the join between the fields
1189 * that this group contain and it's parent to be! The direction should
1190 * be from the parent to the child.
1191 * OBS a null path will cause an exception.
1192 */
1193 private IEntityRelationPath mParentRelationPath;
1194
1195 /*** Default value is false which should result in FULL OUTER JOIN's. */
1196 private boolean mRequired = false;
1197
1198 /*** Container for the fields belonging to this CompositeFieldGroup.*/
1199 private List mFieldDescriptorList = new ArrayList();
1200
1201 /*** Container for the field groups belonging to this CompositeFieldGroup.*/
1202 private List mFieldGroupList = new ArrayList();
1203
1204 /*** The ICompositeEntityDescriptor that is wrapping this field group's
1205 * fields (and maybe fields form other field groups)
1206 *
1207 * OBS! Here we have an Hen and the egg which came first situation!
1208 * Since the field groups are created first and included as one of
1209 * the parameters when constructing the CompositeEntityDescriptor
1210 * this member can't be set until we are done constructing the
1211 * wrapping CompositeEntityDescriptor-object (and is therefor NOT valid
1212 * before the field group (with others) are wrapped up in an
1213 * CompositeEntityDescriptor-object!
1214 */
1215 private CompositeEntityDescriptor mCompositeEntityDescriptor = null;
1216
1217
1218
1219 /*** Creates a CompositeFieldGroup-object.
1220 *
1221 * @param iBaseEntityDescriptor is the base entity that all added fields
1222 * should be belong to. All added child field group will be joined
1223 * to this one.
1224 * @param entityIdentity is the named identity of the created field
1225 * group. It's optional. This will normally be the code name of
1226 * the base entity descriptor unless multiple instances of the
1227 * descriptor exists in the field group structure.
1228 * @param parentRelationPath is the path that connects this group and
1229 * another group i.e. the parent.
1230 * @param required determines whether or not this field group's fields
1231 * are required or not in joins.
1232 * @see ICompositeEntityDescriptor.ICompositeFieldGroup#getRequired()
1233 */
1234 private CompositeFieldGroup(IBaseEntityDescriptor iBaseEntityDescriptor, String entityIdentity, IEntityRelationPath parentRelationPath, boolean required)
1235 {
1236 mIBaseEntityDescriptor = iBaseEntityDescriptor;
1237 mEntityIdentity = entityIdentity;
1238 mParentRelationPath = parentRelationPath;
1239 mRequired = required;
1240 }
1241
1242 /*** Creates a CompositeFieldGroup-object by making a copy of an existing
1243 * ICompositeFieldGroup-structure. It also make a copy of the parameter
1244 * childFieldGroup if not <null>. This new child copy will be added to
1245 * nodeFieldGroup if this parameter isn't <null> or in the <null> case
1246 * to the root field group.
1247 *
1248 * @param rootFieldGroup copied to a new root.
1249 * @param rootRequired
1250 * @param nodeFieldGroup virtual node to add child to.
1251 * @param nodeRequired
1252 * @param childFieldGroup copied child will be added to node (or root).
1253 * @param childRequired
1254 * @param parentToChildPath the relation path between the node (or root) and the child.
1255 * @see org.caleigo.core.ICompositeEntityDescriptor.ICompositeFieldGroup#getRequired() rootRequired, nodeRequired, childRequired.
1256 */
1257 private CompositeFieldGroup(ICompositeFieldGroup rootFieldGroup, boolean rootRequired, ICompositeFieldGroup nodeFieldGroup, boolean nodeRequired, ICompositeFieldGroup childFieldGroup, boolean childRequired, IEntityRelationPath parentToChildPath)
1258 {
1259
1260 this(rootFieldGroup.getBaseEntityDescriptor(), rootFieldGroup.getEntityIdentity(), null, rootRequired);
1261
1262
1263 ICompositeFieldGroup virtualParent = nodeFieldGroup!=null?nodeFieldGroup:rootFieldGroup;
1264 CompositeFieldGroup realParent = null;
1265
1266
1267 Stack stackForCopiedTree = new Stack();
1268 Stack stackFornewTree = new Stack();
1269 stackForCopiedTree.push(rootFieldGroup);
1270 stackFornewTree.push(this);
1271
1272 while (!stackForCopiedTree.empty())
1273 {
1274 ICompositeFieldGroup iCompositeFieldGroup2Copy = (ICompositeFieldGroup)stackForCopiedTree.pop();
1275 CompositeFieldGroup compositeFieldGroupInNewTree = (CompositeFieldGroup)stackFornewTree.pop();
1276
1277 if (virtualParent == iCompositeFieldGroup2Copy)
1278 realParent = compositeFieldGroupInNewTree;
1279
1280
1281 for (int j=0;j<iCompositeFieldGroup2Copy.getFieldCount();j++)
1282 {
1283 compositeFieldGroupInNewTree.addFieldDescriptor(
1284 iCompositeFieldGroup2Copy.getFieldDescriptor(j).getSourceFieldDescriptor(),
1285 iCompositeFieldGroup2Copy.getFieldDescriptor(j).getCodeName(),
1286 iCompositeFieldGroup2Copy.getFieldDescriptor(j).getDisplayName(),
1287 calculateFieldDescriptorFlags(iCompositeFieldGroup2Copy.getFieldDescriptor(j))
1288 );
1289 }
1290
1291
1292
1293
1294
1295
1296 for (int j=0;j<iCompositeFieldGroup2Copy.getChildFieldGroupCount();j++)
1297 {
1298 ICompositeFieldGroup icfg = iCompositeFieldGroup2Copy.getChildFieldGroup(j);
1299 CompositeFieldGroup cfg;
1300 boolean req;
1301 if (nodeFieldGroup == icfg)
1302 req = nodeRequired;
1303 else
1304 req = icfg.getRequired();
1305 cfg = new CompositeFieldGroup(icfg.getBaseEntityDescriptor(),
1306 icfg.getEntityIdentity(),
1307 icfg.getParentRelationPath(),
1308 req);
1309
1310 compositeFieldGroupInNewTree.addChildFieldGroup(this, cfg, true);
1311
1312 stackForCopiedTree.push(icfg);
1313 stackFornewTree.push(cfg);
1314 }
1315 }
1316
1317 if (childFieldGroup != null && realParent!= null)
1318 {
1319 CompositeFieldGroup duplicatedChild = new CompositeFieldGroup(childFieldGroup, childRequired, null, false, null, false, null );
1320 duplicatedChild.mParentRelationPath = parentToChildPath;
1321 realParent.addChildFieldGroup(this, duplicatedChild, true);
1322 }
1323
1324 }
1325
1326
1327
1328 /*** Resolve naming conflicts for the member variable: "mEntityIdentity".
1329 */
1330 private int resolveEntityIdentityConflicts(CompositeFieldGroup rootFieldGroup, boolean resolveEntityIdentityConflicts)
1331 {
1332
1333 int changes = 0;
1334 Iterator groupIterator = rootFieldGroup.getFieldGroups();
1335 HashMap takenIDs = new HashMap();
1336 while (groupIterator.hasNext())
1337 {
1338 CompositeFieldGroup groupBeeingChecked = (CompositeFieldGroup)groupIterator.next();
1339 String entityIdentity2Check = groupBeeingChecked.getEntityIdentity();
1340 if (takenIDs.containsKey(entityIdentity2Check))
1341 {
1342 String oldEntityIdentity = entityIdentity2Check;
1343
1344
1345 do
1346 entityIdentity2Check = increaseStringEndingWithNumber(entityIdentity2Check);
1347 while ( takenIDs.containsKey(entityIdentity2Check) );
1348
1349
1350 if (resolveEntityIdentityConflicts)
1351 {
1352 changes++;
1353 groupBeeingChecked.mEntityIdentity = entityIdentity2Check;
1354 Log.printWarning(this, "Changed the entity identity on " +
1355 "field group from: \""+oldEntityIdentity+"\" to \""+
1356 entityIdentity2Check+"\" due to resolving problems!");
1357 }
1358 else
1359 throw new InvalidDescriptorException("There is a naming " +
1360 "conflict, the entity identity: "+oldEntityIdentity+
1361 " is occupied! Solve this by manually by renaming "+
1362 "it to e.g. " + entityIdentity2Check);
1363 }
1364 takenIDs.put(entityIdentity2Check, null);
1365 }
1366
1367
1368
1369
1370
1371
1372
1373
1374 return changes;
1375 }
1376
1377
1378
1379 /*** Collect all nodes in a field group tree.
1380 * @param fieldGroup root of the field group tree.
1381 * @return an array with all groups sorted in bread first order.
1382 */
1383 private static ICompositeFieldGroup[] getFieldGroupsBreadFirst(ICompositeFieldGroup fieldGroup)
1384 {
1385 return (ICompositeFieldGroup[])getFieldGroupsBreadFirst(fieldGroup, new ArrayList()).toArray(new ICompositeFieldGroup[]{});
1386 }
1387
1388 /*** Help-method, call the tree's nodes recursively in bread first order.
1389 */
1390 private static List getFieldGroupsBreadFirst(ICompositeFieldGroup fieldGroup, List fieldGroupList)
1391 {
1392 fieldGroupList.add(fieldGroup);
1393 for(int j=0; j<fieldGroup.getChildFieldGroupCount(); j++)
1394 fieldGroupList = getFieldGroupsBreadFirst(fieldGroup.getChildFieldGroup(j), fieldGroupList);
1395 return fieldGroupList;
1396 }
1397
1398 /*** Get the index for the composite field descriptor that wraps
1399 * the field descriptor given as parameter.
1400 *
1401 * @param fieldDescriptor a wrapped IFieldDescriptor.
1402 * @return an int representing the index for the
1403 * ICompositeFieldDescriptor that wraps the parameter fieldDescriptor.
1404 * A negative number is returned if no ICompositeFieldDescriptor wraps
1405 * the parameter fieldDescriptor.
1406 */
1407 private int getIndexForWrappedField(IFieldDescriptor fieldDescriptor)
1408 {
1409 int index = this.getFieldCount()-1;
1410 while(index >= 0)
1411 {
1412 if(fieldDescriptor == ((ICompositeFieldDescriptor)this.getFieldDescriptor(index)).getSourceFieldDescriptor())
1413 break;
1414 --index;
1415 }
1416 return index;
1417 }
1418
1419 /*** Determents if a primary-key is present among the fields belonging to
1420 * this field group.
1421 */
1422 private boolean determentIfPrimaryKeyIsPresent()
1423 {
1424 IFieldDescriptor[] idFieldsThatNeed2BePresent = Relations.getIdentityFields(this.getBaseEntityDescriptor());
1425 return determentIfAllFieldsArePresent(idFieldsThatNeed2BePresent);
1426 }
1427
1428 /*** Determents if this field group's wrapped fields cover all fields in
1429 * the array given as parameter.
1430 */
1431 private boolean determentIfAllFieldsArePresent(IFieldDescriptor[] fieldsThatNeed2BePresent)
1432 {
1433 for(int i=0;i<fieldsThatNeed2BePresent.length;i++)
1434 {
1435 boolean presentInGroup = false;
1436 for(int j=0;j<this.getFieldCount();j++)
1437 {
1438 presentInGroup = false;
1439 if( fieldsThatNeed2BePresent[i] == ((ICompositeFieldDescriptor)this.getFieldDescriptor(j)).getSourceFieldDescriptor() )
1440 {
1441 presentInGroup = true;
1442 break;
1443 }
1444 }
1445 if(presentInGroup == false)
1446 return false;
1447 }
1448 return true;
1449 }
1450
1451 /*** After a call to this method the object should be considered
1452 * immutable, therefore each mutable method must call the
1453 * checkForHolder method.
1454 * @see #checkForHolder()
1455 */
1456 private void setICompositeEntityDescriptor(CompositeEntityDescriptor compositeEntityDescriptor)
1457 {
1458 checkForHolder();
1459 mCompositeEntityDescriptor = compositeEntityDescriptor;
1460 }
1461
1462 /*** Method to make sure that the object is in a stable condition.
1463 * Should be called firstly in every mutable method to make sure that
1464 * this object hasn't been wrapped by an CompositeEntityDescriptor.
1465 */
1466 private void checkForHolder()
1467 {
1468 if (mCompositeEntityDescriptor != null)
1469 throw new RuntimeException("Mutable call to a CompositeFieldGroup that already belongs to a wrapping CompositeEntityDescriptor!");
1470 }
1471
1472
1473
1474
1475
1476 /*** Adds the passed field to this field group.
1477 *
1478 * @param field the fieldDescriptor to add.
1479 *
1480 * @see #addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1481 */
1482 private void addFieldDescriptor(IFieldDescriptor field)
1483 {
1484
1485 this.addFieldDescriptor(field, field.getCodeName(), field.getDisplayName());
1486 }
1487
1488 /*** Adds the passed field to this field group.
1489 * Unwapping N Wrapping of fields are always done in this implementation!
1490 *
1491 * @param field the fieldDescriptor to add.
1492 * @param codeName the new codeName
1493 * @param displayName the new displayName
1494 *
1495 * @see #addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1496 */
1497 private void addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName)
1498 {
1499
1500
1501 int flags;
1502
1503
1504 if (field instanceof ICompositeFieldDescriptor)
1505 flags = calculateFieldDescriptorFlags( ((ICompositeFieldDescriptor)field).getSourceFieldDescriptor() );
1506 else
1507 flags = calculateFieldDescriptorFlags( field );
1508
1509 this.addFieldDescriptor(field, codeName, displayName, flags);
1510 }
1511
1512 /*** Adds the passed field to this field group.
1513 * Unwapping N Wrapping of fields are always done in this implementation!
1514 *
1515 * @param field the fieldDescriptor to add.
1516 * @param codeName the new codeName.
1517 * @param displayName the new displayName.
1518 * @param flags an int that represent the flags that are to be set for
1519 * this field.
1520 *
1521 * @see IFieldDescriptor#IDENTITY_FIELD
1522 * @see IFieldDescriptor#NATURAL_ORDER
1523 * @see IFieldDescriptor#REQUIRED
1524 * @see IFieldDescriptor#INDEXED
1525 * @see IFieldDescriptor#AUTOGEN
1526 * @see IFieldDescriptor#NAME_FIELD
1527 * @see IFieldDescriptor#OVERVIEW_FIELD
1528 * @see IFieldDescriptor#HINT_FIELD
1529 * @see IFieldDescriptor#HIDDEN_FIELD
1530 * @see IFieldDescriptor#READ_ONLY_FIELD
1531 */
1532 private void addFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1533 {
1534 checkForHolder();
1535
1536
1537 if (field instanceof ICompositeFieldDescriptor)
1538 field = ((ICompositeFieldDescriptor)field).getSourceFieldDescriptor();
1539
1540
1541 if ( field.getEntityDescriptor()!=this.getBaseEntityDescriptor() )
1542 throw new InvalidFieldException("The field must be part of the " +
1543 "\"base entity descriptor\": "+
1544 this.getBaseEntityDescriptor()+"!");
1545
1546
1547 ICompositeEntityDescriptor.ICompositeFieldDescriptor wrappedField =
1548 this.new CompositeFieldDescriptor(field, codeName, displayName, flags);
1549 mFieldDescriptorList.add(wrappedField);
1550 }
1551
1552 /*** Add field group children to this field group.
1553 * @param rootFieldGroup is used to solve entity-identity conflicts (if
1554 * any exist and the parameter resolveEntityIdentityConflicts == true).
1555 * @param fieldGroup the field group to add.
1556 * @param resolveEntityIdentityConflicts determines if solving of
1557 * eventually entity-identity conflicts are to be solved, if false and
1558 * conflicts exist an exception is thrown.
1559 */
1560 private void addChildFieldGroup(CompositeFieldGroup rootFieldGroup, CompositeFieldGroup fieldGroup, boolean resolveEntityIdentityConflicts)
1561 {
1562 checkForHolder();
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572 if( fieldGroup.getParentRelationPath()==null ||
1573 !( ( fieldGroup.getParentRelationPath().getFirstEntityDescriptor()==this.getBaseEntityDescriptor() &&
1574 fieldGroup.getParentRelationPath().getLastEntityDescriptor()==fieldGroup.getBaseEntityDescriptor() )
1575 ) )
1576 throw new InvalidRelationException("Relation path must link parent and child entities " +
1577 "and the direction must be FROM the parent TO the child!");
1578 if ( fieldGroup.getParentRelationPath().getRelationCount() == 0)
1579 throw new InvalidRelationException("Relation path mustn't be a \"null\"-path!");
1580
1581 mFieldGroupList.add(fieldGroup);
1582
1583
1584 int changes = resolveEntityIdentityConflicts(fieldGroup, resolveEntityIdentityConflicts);
1585 if (changes > 0)
1586 Log.print(this, changes+" changes were made to solve naming conflicts with field group's entity identities!");
1587
1588 }
1589
1590
1591
1592
1593 /*** Returns the entity descriptor of the field group. All fields must
1594 * belong to that entity descriptor. A composite entity may however
1595 * have several fieldgroups with the same entity descriptor, but all
1596 * must have unique entity identity strings.
1597 */
1598 public IBaseEntityDescriptor getBaseEntityDescriptor()
1599 {
1600 return mIBaseEntityDescriptor;
1601 }
1602
1603 /*** Returns the named identity of the called field groups entity
1604 * descriptor. This will normally be the code name of the entity
1605 * descriptor unless multiple instances of the descriptor exists.
1606 */
1607 public String getEntityIdentity()
1608 {
1609 if(mEntityIdentity!=null)
1610 return mEntityIdentity;
1611 else
1612 return this.getBaseEntityDescriptor().getCodeName();
1613 }
1614
1615 /*** Returns the relation path from the parent field group to this field
1616 * group.
1617 */
1618 public IEntityRelationPath getParentRelationPath()
1619 {
1620 return mParentRelationPath;
1621 }
1622
1623
1624 /*** Returns the number of field descriptors that the called field group
1625 * contains. This includes both data and calculated field descriptors.
1626 */
1627 public int getFieldCount()
1628 {
1629 return mFieldDescriptorList.size();
1630 }
1631
1632 /*** Returns a specific field descriptor from the the called field group
1633 * by its ordial index. May cast OutOfBoundException.
1634 */
1635 public ICompositeFieldDescriptor getFieldDescriptor(int index)
1636 {
1637 return (ICompositeFieldDescriptor)mFieldDescriptorList.get(index);
1638 }
1639
1640 /*** Returns the number of field groups that the called field group
1641 * contains. This includes both data and calculated field descriptors.
1642 */
1643 public int getChildFieldGroupCount()
1644 {
1645 return mFieldGroupList.size();
1646 }
1647
1648 /*** Returns a specific field group from the the called field group
1649 * by its ordial index. May cast OutOfBoundException.
1650 */
1651 public ICompositeFieldGroup getChildFieldGroup(int index)
1652 {
1653 return (ICompositeFieldGroup)mFieldGroupList.get(index);
1654 }
1655
1656 public boolean getRequired()
1657 {
1658 return mRequired;
1659 }
1660
1661 /*** @return an iterator that iterates over all field group's that has
1662 * this field group as root.
1663 */
1664 public java.util.Iterator getFieldGroups()
1665 {
1666 return org.caleigo.toolkit.util.Iterators.iterate(getFieldGroupsBreadFirst(this));
1667 }
1668
1669
1670
1671
1672 public String toString()
1673 {
1674 return "FieldGroup-"+this.getEntityIdentity();
1675 }
1676
1677
1678
1679
1680
1681
1682 /*** The ICompositeFieldDescriptor wraps another field descriptor and in
1683 * many ways acts as a proxy for it. It can be used to alter properties
1684 * such as code and display names.
1685 */
1686 protected class CompositeFieldDescriptor implements ICompositeFieldDescriptor
1687 {
1688
1689 private IFieldDescriptor mRemoteFieldDescriptor;
1690 private String mCodeName;
1691 private String mDisplayName;
1692 private int mFlags;
1693
1694
1695
1696 protected CompositeFieldDescriptor(IFieldDescriptor field, String codeName, String displayName, int flags)
1697 {
1698 mRemoteFieldDescriptor = field;
1699 mCodeName = codeName;
1700 mDisplayName = displayName;
1701 mFlags = flags;
1702 }
1703
1704
1705
1706 /*** Return the code name for the field. The code name should be used
1707 * for programatic identification. This is safer and recommended
1708 * compared to the use of field indexes. The code name is also the
1709 * name of the data property for the field in entities.
1710 */
1711 public String getCodeName()
1712 {
1713 return mCodeName;
1714 }
1715
1716 /*** Returns the source name for the field. The source name is an
1717 * identifier for the field in the data service layer.
1718 */
1719 public String getSourceName()
1720 {
1721 return mRemoteFieldDescriptor.getSourceName();
1722 }
1723
1724 /*** Returns the display name for the field. The reurned value has been
1725 * passed through the ResourceProvider to allow localisation.
1726 */
1727 public String getDisplayName()
1728 {
1729 return mDisplayName;
1730 }
1731
1732 /*** Returns the data type for the field.
1733 */
1734 public DataType getDataType()
1735 {
1736 return mRemoteFieldDescriptor.getDataType();
1737 }
1738
1739 /*** Returns the length of the data type if the type is a scalar type.
1740 */
1741 public int getLength()
1742 {
1743 return mRemoteFieldDescriptor.getLength();
1744 }
1745
1746 /*** Return the default value for the field or null if none is set.
1747 * This default value will automaticalli be set as the default value
1748 * for created entities that the field is part of.
1749 */
1750 public Object getDefaultValue()
1751 {
1752 return mRemoteFieldDescriptor.getDefaultValue();
1753 }
1754
1755 public boolean isIdentityField()
1756 {
1757 return (mFlags & IDENTITY_FIELD)!=0;
1758 }
1759
1760 public boolean isReferenceField()
1761 {
1762 return this.getReferenceFieldRelation()!=null;
1763 }
1764
1765 public boolean isRequired()
1766 {
1767 return (mFlags & REQUIRED)!=0;
1768 }
1769
1770 public boolean isIndexed()
1771 {
1772 return (mFlags & INDEXED)!=0;
1773 }
1774
1775 public boolean isAutoGenerated()
1776 {
1777 return (mFlags & AUTOGEN)!=0;
1778 }
1779
1780 public boolean isNameField()
1781 {
1782 return (mFlags & NAME_FIELD)!=0;
1783 }
1784
1785 public boolean isOverviewField()
1786 {
1787 return (mFlags & OVERVIEW_FIELD)!=0;
1788 }
1789
1790 public boolean isHintField()
1791 {
1792 return (mFlags & HINT_FIELD)!=0;
1793 }
1794
1795 public boolean isHiddenField()
1796 {
1797 return (mFlags & HIDDEN_FIELD)!=0;
1798 }
1799
1800 /*** @return true
1801 * if the the read only flag (READ_ONLY_FIELD) is set for this field
1802 * OR
1803 * if the field's field group's EntityDescriptor isn't editable
1804 * OR
1805 * if the field's field group's primary key isn't present among the
1806 * fields.
1807 *
1808 * OBS, Watch out the behaviour when this method are called before
1809 * the field descriptor's field group's base entity descriptor's
1810 * data source descriptor has been initialized!
1811 */
1812 public boolean isReadOnly()
1813 {
1814 boolean readonlyFlag =((mFlags & READ_ONLY_FIELD)!=0);
1815 boolean groupisNOTEditable;
1816
1817
1818 if(mIBaseEntityDescriptor.getDataSourceDescriptor() == null)
1819 groupisNOTEditable = false;
1820 else
1821 groupisNOTEditable = (CompositeFieldGroup.this.mIBaseEntityDescriptor.isEditable() == false) ;
1822 boolean keyForGroupisNOTPresent = (CompositeFieldGroup.this.determentIfPrimaryKeyIsPresent() == false);
1823 return readonlyFlag || groupisNOTEditable || keyForGroupisNOTPresent;
1824
1825 }
1826
1827 /*** Access method that returns true if the field is the natural order
1828 * field for the entities it is a part of. If no natural order field is
1829 * defined the identity field will be used for natural order.
1830 */
1831 public boolean isNaturalOrder()
1832 {
1833 return (mFlags & NATURAL_ORDER)!=0;
1834 }
1835
1836 /*** Access method that returns the entity descriptor that the described
1837 * field is part of.
1838 */
1839 public IEntityDescriptor getEntityDescriptor()
1840 {
1841 return CompositeFieldGroup.this.mCompositeEntityDescriptor;
1842 }
1843
1844 /*** This method returns true if the called field descriptor can validate
1845 * data described by the field in the context of the provided IDataProvider
1846 * object. The minimum requiriment is that described field data can be
1847 * provided but in some cases other data resources is also required.
1848 */
1849 public boolean canValidate(IDataProvider dataProvider)
1850 {
1851
1852 return mRemoteFieldDescriptor.canValidate(dataProvider);
1853 }
1854
1855 /*** This method validates the provided data object as described by the
1856 * called field descriptor in the context of the provided IDataProvider
1857 * object.
1858 */
1859 public ValidationResult validateData(Object data, IDataProvider dataProvider)
1860 {
1861 ValidationResult result = ValidationResult.VALID_RESULT;
1862
1863
1864 result = this.performBaseValidation(data);
1865
1866
1867
1868
1869
1870
1871 return result;
1872 }
1873
1874
1875 /*** Performs basic validations that are relevant for all fields like
1876 * checking for null values in reguired field data.
1877 */
1878 public ValidationResult performBaseValidation(Object dataValue)
1879 {
1880
1881 if(dataValue==null && this.isRequired() && !this.isAutoGenerated())
1882 return new ValidationResult(this, ResourceProvider.getString(this.getEntityDescriptor().getCodeName(), mCodeName+".RequiredMSG", "The "+this.getDisplayName()+" field may not be empty."));
1883
1884
1885 if(this.getDataType()==DataType.STRING && dataValue!=null && ((String)dataValue).length()>this.getLength() && this.getLength()>0)
1886 return new ValidationResult(this, ResourceProvider.getString(this.getEntityDescriptor().getCodeName(), mCodeName+".ToLongMSG", "The "+this.getDisplayName()+" field is to long. It may only contain "+this.getLength()+" characters."));
1887
1888 return ValidationResult.VALID_RESULT;
1889 }
1890
1891
1892 /*** Access method that returns the field relation that the called field
1893 * is a reference to. If the called field descriptor is not a reference
1894 * field then null is returned.
1895 */
1896 public IFieldRelation getReferenceFieldRelation()
1897 {
1898 IFieldRelation refRelation = null;
1899 java.util.Iterator it = this.getFieldRelations();
1900 while(refRelation==null && it.hasNext())
1901 {
1902 IFieldRelation relation = (IFieldRelation)it.next();
1903 if(relation.getReferenceField()==this)
1904 refRelation = relation;
1905 }
1906 return refRelation;
1907 }
1908
1909 /*** Access method that returns an iterator for all IFieldRelation
1910 * objects that the called field descriptor is part of.
1911 */
1912 public java.util.Iterator getFieldRelations()
1913 {
1914 Iterator it = this.getEntityDescriptor().getEntityRelations();
1915 int count = 0;
1916 while(it.hasNext())
1917 {
1918 IEntityRelation iEntityRelations = (IEntityRelation)it.next();
1919 count = count + iEntityRelations.getFieldCount();
1920 }
1921
1922 IFieldRelation iFieldRelations[] = new IFieldRelation[count];
1923
1924 it = this.getEntityDescriptor().getEntityRelations();
1925 int c = 0;
1926 while(it.hasNext())
1927 {
1928 IEntityRelation iEntityRelation = (IEntityRelation)it.next();
1929 for (int i = 0; i < iEntityRelation.getFieldCount(); i++)
1930 {
1931 iFieldRelations[c++] = iEntityRelation.getFieldRelation(i);
1932
1933 }
1934 }
1935
1936 return org.caleigo.toolkit.util.Iterators.iterate(iFieldRelations);
1937 }
1938
1939
1940
1941 /*** Access method to the wrapped/remote field descriptor.
1942 */
1943 public IFieldDescriptor getRemoteFieldDescriptor()
1944 {
1945 return mRemoteFieldDescriptor;
1946 }
1947
1948 /*** Access method to the source field descriptor. This corresponds to
1949 * looping through all remotes to the first non composite field.
1950 */
1951 public IFieldDescriptor getSourceFieldDescriptor()
1952 {
1953 if(mRemoteFieldDescriptor instanceof ICompositeFieldDescriptor)
1954 return ((ICompositeFieldDescriptor)mRemoteFieldDescriptor).getSourceFieldDescriptor();
1955 else
1956 return mRemoteFieldDescriptor;
1957 }
1958
1959 /*** @return The ICompositeFieldGroup this composite field descriptor
1960 * belongs to.
1961 */
1962 public ICompositeFieldGroup getFieldGroup()
1963 {
1964 return CompositeFieldGroup.this;
1965 }
1966
1967 public String toString()
1968 {
1969 return "Composite-"+getSourceFieldDescriptor();
1970 }
1971
1972
1973
1974
1975 protected class FieldRelationIterator implements java.util.Iterator
1976 {
1977 private int mEntityRelationIndex;
1978 private int mFieldRelationIndex;
1979 private Object mNextObject;
1980
1981 public FieldRelationIterator()
1982 {
1983 mEntityRelationIndex = 0;
1984 mFieldRelationIndex = 0;
1985 this.next();
1986 }
1987
1988 public Object next()
1989 {
1990 Object retObject = mNextObject;
1991
1992 mNextObject = null;
1993 while(mNextObject==null && mEntityRelationIndex<getEntityDescriptor().getEntityRelationCount())
1994 {
1995
1996 while(mNextObject==null && mFieldRelationIndex<getEntityDescriptor().getEntityRelation(mEntityRelationIndex).getFieldCount())
1997 {
1998
1999 IFieldRelation rel = getEntityDescriptor().getEntityRelation(mEntityRelationIndex).getFieldRelation(mFieldRelationIndex);
2000 if(rel.getReferenceField()==CompositeFieldDescriptor.this || rel.getTargetField()==CompositeFieldDescriptor.this)
2001 mNextObject = rel;
2002
2003
2004 mFieldRelationIndex++;
2005 }
2006
2007
2008 if(mFieldRelationIndex>=getEntityDescriptor().getEntityRelation(mEntityRelationIndex).getFieldCount())
2009 {
2010 mEntityRelationIndex++;
2011 mFieldRelationIndex = 0;
2012 }
2013 }
2014
2015 return retObject;
2016 }
2017
2018 public boolean hasNext()
2019 {
2020 return mNextObject!=null;
2021 }
2022
2023 public void remove()
2024 {
2025 throw new java.lang.UnsupportedOperationException();
2026 }
2027 }
2028
2029
2030 }
2031
2032 }
2033
2034
2035
2036
2037
2038 /*** Basic implementation of the IFieldRelation for Composite Field Relations.
2039 * @see IFieldRelation
2040 * @see FieldRelation
2041 *
2042 * This implementation of the IFieldRelation requires that the classes
2043 * of the linked descriptors has been loaded and initialized.
2044 *
2045 * @author Niklas Norberg
2046 * @version 1.00
2047 *
2048 *//*
2049 *
2050 * WHEN WHO WHY & WHAT
2051 * -----------------------------------------------------------------------------
2052 * 2003-12-03 Niklas Norberg Creation: copied FieldRelation.java and
2053 * outcommented code.
2054 */
2055 protected static class CompositeFieldRelation implements IFieldRelation
2056 {
2057
2058 private IEntityRelation mEntityRelation;
2059
2060
2061
2062 private IFieldDescriptor mReferenceField;
2063 private IFieldDescriptor mTargetField;
2064
2065
2066
2067 /*** Constructor for FieldRelation. Note package scope to avoid missuse.
2068 */
2069 CompositeFieldRelation(IFieldDescriptor referenceFieldDescriptor,
2070 IFieldDescriptor targetFieldDescriptor)
2071 {
2072 mReferenceField = referenceFieldDescriptor;
2073 mTargetField = targetFieldDescriptor;
2074 }
2075
2076
2077 public boolean equals(Object obj)
2078 {
2079 if(obj==null || !(obj instanceof IFieldRelation))
2080 return false;
2081
2082 IFieldRelation relation = (IFieldRelation)obj;
2083
2084 return this.getReferenceField()==relation.getReferenceField()
2085 && this.getTargetField()==relation.getTargetField();
2086 }
2087
2088
2089
2090 /*** Access method that return the IEntityRelation that the called
2091 * IFieldRelation is a part of.
2092 */
2093 public IEntityRelation getEntityRelation()
2094 {
2095 return mEntityRelation;
2096 }
2097
2098 /*** Access method that return the IFieldDescriptor that is the
2099 * source/reference field that addresses another field. In RDB terms the
2100 * reference field is or should be defined as a foreign key.
2101 */
2102 public IFieldDescriptor getReferenceField()
2103 {
2104 return mReferenceField;
2105 }
2106
2107 /*** Access method that return the IFieldDescriptor that is the
2108 * target field that are addressed by the reference field. In RDB terms the
2109 * target field is usually a primary-key field.
2110 */
2111 public IFieldDescriptor getTargetField()
2112 {
2113 return mTargetField;
2114 }
2115
2116
2117
2118 /*** Mutation method with restricted access to avoid missuse.
2119 */
2120 void setEntityRelation(IEntityRelation relation)
2121 {
2122 mEntityRelation = relation;
2123 }
2124
2125 }
2126
2127
2128
2129 /*** The default store action used by the CompositeEntityDescriptor class.
2130 * @author Niklas Norberg
2131 */
2132 public static class CompositeStoreEntityAction extends AbstractTransactionEntityAction implements IUnaryEntityAction
2133 {
2134
2135 IEntity insertedBaseEntity = null;
2136
2137
2138 public CompositeStoreEntityAction(ICompositeEntityDescriptor entityDescriptor)
2139 {
2140 super(entityDescriptor, STORE_ACTION, "Store", null);
2141 }
2142
2143
2144
2145
2146 /*** Prepare method for the action where the action is expected to add
2147 * the data operations needed to perform the action.
2148 */
2149 public void prepareTransaction(IDataBundle data, IDataTransaction transaction)
2150 {
2151 IEntity iEntity = (IEntity)data.getData(0);
2152
2153 this.prepareEntityData(iEntity);
2154 this.validateData(data);
2155
2156
2157
2158
2159
2160
2161 ICompositeEntityDescriptor iCompositeEntityDescriptor;
2162 if( !(iEntity.getEntityDescriptor() instanceof ICompositeEntityDescriptor) )
2163 throw new InvalidDescriptorException("The entities descriptor must be an ICompositeEntityDescriptor");
2164 else
2165 iCompositeEntityDescriptor = (ICompositeEntityDescriptor)iEntity.getEntityDescriptor();
2166
2167
2168 if(iEntity.isPersistent())
2169 {
2170
2171
2172 for (Iterator groupIterator = iCompositeEntityDescriptor.getRootFieldGroup().getFieldGroups(); groupIterator.hasNext();)
2173 {
2174 ICompositeEntityDescriptor.ICompositeFieldGroup group = (ICompositeEntityDescriptor.ICompositeFieldGroup) groupIterator.next();
2175
2176
2177 ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor;
2178
2179
2180 int posInGroup = 0;
2181 while(posInGroup < group.getFieldCount())
2182 {
2183 compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)group.getFieldDescriptor(posInGroup);
2184 if( iEntity.isFieldDirty( compositeFieldDescriptor ) )
2185 break;
2186 posInGroup++;
2187 }
2188
2189 if( posInGroup < group.getFieldCount() )
2190 {
2191
2192
2193 CompositeQualifier key = getKeyForFieldGroup(group, iEntity);
2194
2195
2196 IEntity baseEntity = group.getBaseEntityDescriptor().loadEntity(key);
2197
2198
2199 while(posInGroup < group.getFieldCount())
2200 {
2201 compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)group.getFieldDescriptor(posInGroup);
2202 if( iEntity.isFieldDirty( compositeFieldDescriptor ) )
2203 baseEntity.setData(compositeFieldDescriptor.getSourceFieldDescriptor(),iEntity.getData(compositeFieldDescriptor));
2204 posInGroup++;
2205 }
2206
2207
2208 transaction.addStore(baseEntity);
2209
2210 }
2211 }
2212 }
2213 else
2214 {
2215
2216
2217 ICompositeEntityDescriptor.ICompositeFieldGroup rootGroup = iCompositeEntityDescriptor.getRootFieldGroup();
2218
2219
2220 IEntity baseEntity = rootGroup.getBaseEntityDescriptor().createEntity();
2221
2222
2223 int posInGroup = 0;
2224 while(posInGroup < rootGroup.getFieldCount())
2225 {
2226 ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor;
2227 compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)rootGroup.getFieldDescriptor(posInGroup);
2228 if( iEntity.isFieldDirty( compositeFieldDescriptor ) )
2229 baseEntity.setData(compositeFieldDescriptor.getSourceFieldDescriptor(),iEntity.getData(compositeFieldDescriptor));
2230 posInGroup++;
2231 }
2232
2233
2234 transaction.addStore(baseEntity);
2235 insertedBaseEntity = baseEntity;
2236
2237 }
2238
2239 }
2240
2241 /*** Clean up method for the action where the action can clean up data
2242 * operations, if necessary via an overriding method in an implementing
2243 * class, which were performed by the action. E.g. resetting flags etc.
2244 */
2245 public void cleanUpTransaction(IDataBundle data, IDataTransaction transaction)
2246 {
2247 IEntity entity = (IEntity)data.getData(0);
2248
2249
2250 ICompositeEntityDescriptor compositeEntityDescriptor;
2251 if( !(entity.getEntityDescriptor() instanceof ICompositeEntityDescriptor) )
2252 throw new InvalidDescriptorException("The entities descriptor must be an ICompositeEntityDescriptor");
2253 else
2254 compositeEntityDescriptor = (ICompositeEntityDescriptor)entity.getEntityDescriptor();
2255
2256 if(insertedBaseEntity != null)
2257 {
2258
2259 for (int i = 0; i < compositeEntityDescriptor.getRootFieldGroup().getFieldCount(); i++)
2260 {
2261 ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)compositeEntityDescriptor.getRootFieldGroup().getFieldDescriptor(i);
2262 if(compositeFieldDescriptor.isIdentityField())
2263 entity.setData(compositeFieldDescriptor ,insertedBaseEntity.getData(compositeFieldDescriptor.getSourceFieldDescriptor()));
2264 }
2265 insertedBaseEntity = null;
2266 }
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294 entity.refresh();
2295
2296 entity.setStatusFlag(IEntity.PERSISTENT);
2297 entity.clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY);
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313 }
2314
2315 /*** This method is called by the action objects constructor once and only
2316 * once. The method is expected to define the action data requriments
2317 * (IDataBundleDescriptor) using the diferent add methods for descriptors
2318 * defined in this class.
2319 */
2320 public final void defineDataDescriptor()
2321 {
2322 this.addEntityDescriptor(this.getEntityDescriptor(), this.getEntityDescriptor().getCodeName(), this.getEntityDescriptor().getDisplayName(), null);
2323 }
2324
2325
2326 public void perform(IEntity entity)
2327 {
2328 IDataBundle data = this.getDataBundleDescriptor().createActionData();
2329 data.setData(0, entity);
2330 this.perform(data);
2331 }
2332
2333
2334
2335 /*** Simple help method that is called when an action are triggered
2336 * emmidiatelly before the action is validated. The method is
2337 * provided for optional override and does nothing by default.
2338 */
2339 protected void prepareEntityData(IEntity entity)
2340 {
2341 }
2342
2343 }
2344
2345
2346
2347 /*** The default delete action used by the CompositeEntityDescriptor class.
2348 * @author Niklas Norberg
2349 */
2350 public static class CompositeDeleteEntityAction extends AbstractTransactionEntityAction implements IUnaryEntityAction
2351 {
2352
2353 public CompositeDeleteEntityAction(ICompositeEntityDescriptor entityDescriptor)
2354 {
2355 super(entityDescriptor, DELETE_ACTION, "Delete", null);
2356 }
2357
2358
2359
2360 /*** Prepare method for the action where the action is expected to add
2361 * the data operations needed to perform the action.
2362 */
2363 public void prepareTransaction(IDataBundle data, IDataTransaction transaction)
2364 {
2365 if(((IEntity)data.getData(0)).isPersistent())
2366 {
2367 IEntity iEntity = (IEntity)data.getData(0);
2368
2369
2370
2371
2372
2373 ICompositeEntityDescriptor iCompositeEntityDescriptor;
2374 if( !(iEntity.getEntityDescriptor() instanceof ICompositeEntityDescriptor) )
2375 throw new InvalidDescriptorException("The entities descriptor must be an ICompositeEntityDescriptor");
2376 else
2377 iCompositeEntityDescriptor = (ICompositeEntityDescriptor)iEntity.getEntityDescriptor();
2378
2379 ICompositeEntityDescriptor.ICompositeFieldGroup rootGroup = iCompositeEntityDescriptor.getRootFieldGroup();
2380
2381
2382 CompositeQualifier key = getKeyForFieldGroup(rootGroup, iEntity);
2383
2384
2385 IEntity baseEntity = rootGroup.getBaseEntityDescriptor().loadEntity(key);
2386
2387
2388 transaction.addDelete(baseEntity);
2389 }
2390 }
2391
2392 /*** Clean up method for the action where the action can clean up data
2393 * operations, if necessary via an overriding method in an implementing
2394 * class, which were performed by the action. E.g. resetting flags etc.
2395 */
2396 public void cleanUpTransaction(IDataBundle data, IDataTransaction transaction)
2397 {
2398 IEntity iEntity = (IEntity)data.getData(0);
2399 iEntity.clearStatusFlag(IEntity.PERSISTENT);
2400 }
2401
2402 /*** This method is called by the action objects constructor once and only
2403 * once. The method is expected to define the action data requriments
2404 * (IDataBundleDescriptor) using the diferent add methods for descriptors
2405 * defined in this class.
2406 */
2407 public final void defineDataDescriptor()
2408 {
2409 this.addEntityDescriptor(this.getEntityDescriptor(), this.getEntityDescriptor().getCodeName(), this.getEntityDescriptor().getDisplayName(), null);
2410 }
2411
2412
2413
2414 public void perform(IEntity entity)
2415 {
2416 IDataBundle data = this.getDataBundleDescriptor().createActionData();
2417 data.setData(0, entity);
2418 this.perform(data);
2419 }
2420
2421 }
2422
2423
2424 public static CompositeQualifier getKeyForFieldGroup(
2425 ICompositeEntityDescriptor.ICompositeFieldGroup group,
2426 IEntity iEntity)
2427 {
2428
2429 CompositeQualifier key = new CompositeQualifier();
2430 IFieldDescriptor[] keyFieldDescriptors = Relations.getIdentityFields(group.getBaseEntityDescriptor());
2431 for (int k = 0; k < keyFieldDescriptors.length; k++)
2432 for(int i=0;i<group.getFieldCount();i++)
2433 {
2434 ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor;
2435 compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)group.getFieldDescriptor(i);
2436 if( compositeFieldDescriptor.getSourceFieldDescriptor() == keyFieldDescriptors[k] )
2437 {
2438 key.add(new RelationQualifier(keyFieldDescriptors[k], iEntity.getData(compositeFieldDescriptor)));
2439 break;
2440 }
2441 }
2442 return key;
2443 }
2444
2445 }
2446
2447