View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      // Data members ------------------------------------------------------------
87      private CompositeFieldGroup mRootFieldGroup;
88      private Qualifier mDefaultQualifier = null;
89      
90      // Constructors ------------------------------------------------------------
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     {//1    //call constructor 99
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     {//2    //call constructor 99
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     {//3    //call constructor 99
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     {//99    //call super-constructor in AbstractEntityDescriptor
140         super(codeName, rootFieldGroup.getBaseEntityDescriptor().getSourceName(), displayName, 
141             CustomEntity.class.getName(),
142             dataSourceClassName,
143             //can't do (due to hen and egg situation):
144             //rootFieldGroup.getBaseEntityDescriptor().getDataSourceDescriptor()!=null?rootFieldGroup.getBaseEntityDescriptor().getDataSourceDescriptor().getClass().getName():null,
145             entityType,
146             buildFieldArray(rootFieldGroup),//throws InvalidFieldException if the root has no fields 
147             buildRelationArray(rootFieldGroup)
148             );
149             
150         mRootFieldGroup = rootFieldGroup;
151         this.setICompositeEntityDescriptor();
152         
153         //nno-todo fix setting of default qualifiers 
154 //        if(qualifiersTargetingRootGroup == null)
155 //            mDefaultQualifier = null;
156 //        else
157 //            mDefaultQualifier = CompositeEntityDescriptor.makeDefaultQualifer(qualifiersTargetingRootGroup);
158         
159         //set flags and cache time
160         int cacheTime = Integer.MAX_VALUE;
161         int sizeFlag = 0;
162         //this.setFlags(SELECTABLE OR LISTABLE OR CACHEABLE OR SCANABLE OR 0);
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         * Due to a a the hen and the egg which came first situation we can't
176         * make these calls now the solution choosen was to skip these calls
177         * here and overriding of the following methods:
178         * isCreatable()
179         * isEditable()
180         * isDeletable()
181         *
182 		int changeTypeFlags = this.calculateEditableModeFlag() |
183 							 this.calculateCreatableModeFlag() |
184 							 this.calculateDelatableModeFlag();
185         int changeTypeFlags = EDITABLE | CREATABLE | DELETABLE;
186 		this.setFlags( sizeFlag | changeTypeFlags );
187          * 
188          * therefor we just set the size flag:
189          */
190         this.setFlags( sizeFlag );
191         
192         this.setCacheTime(cacheTime);
193         
194         //adjust flags for fields
195         this.setFieldDescriptorFlags();
196         
197         //dbg System.out.println(printFlags(this));
198 
199     }
200     
201     // Mutable method ----------------------------------------------------------
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     // Static Constructor help methods -----------------------------------------
220     
221     
222 //    private static Qualifier makeDefaultQualifer(Qualifier[] qualifiersTargetingRootGroup)
223 //    {
224 //        for (int i = 0; i < qualifiersTargetingRootGroup.length; i++)
225 //        {
226 //            Qualifier qualifier = qualifiersTargetingRootGroup[i];            
227 //        }
228 //        return null;
229 //    }
230      
231     /*** Constructor help-method */
232     private static CompositeFieldGroup createRootFieldGroup(IFieldDescriptor[] fieldDescriptors)
233     {
234         //Check parameter:
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         {//make a copy
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         {//create a new root group and make a selection
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         //make joins
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             //Add all fields from the base entity descriptor.
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         // Scan and add selected field descriptors.
301         for(int j=0; j<selectedFieldDescriptors.length; j++)
302         {
303             // Ignore field if not valid or already added.
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         // Scan and add joining field descriptors.
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                 // Ignore field if already added.
349                 if( fieldMap.containsKey(baseField2Join) )
350                     continue;
351                             
352                 // Get relevant field group or create a new one.
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     // Static help methods -----------------------------------------------------
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         // call recursively
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         //get lists with matching field groups with all it's relations.
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                 //Copy field relations
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                         //link 2 base entity descriptor i.e. 2 a slave entity!
445                         targetField = (ICompositeFieldDescriptor)fieldGroup.getFieldDescriptor(index);
446                     }
447                     else
448                     {
449                         //don't add relaton
450 //                        Log.print(null, "\n\n relation not added, targetField: " +
451 //                        targetField.getSourceName() + " \n\n");
452                         continue entityRelationTargetFieldLabel;
453                     }
454                     
455                     iFieldRelations[j] = new CompositeEntityDescriptor.CompositeFieldRelation(referenceField, targetField);
456                 }
457                 //obs the display names
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                 //Copy field relations
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                         //don't add relaton
491 //                        Log.print(null, "\n\n relation not added, referenceField: " +
492 //                                referenceField.getSourceName() + " \n\n");
493                         continue entityRelationReferenceFieldLabel;
494                     }
495                     
496                     iFieldRelations[j] = new CompositeEntityDescriptor.CompositeFieldRelation(referenceField, targetField);
497                 }
498                 //obs the display names
499                 entityRelationList.add(AbstractEntityDescriptor.createEntityRelation(iFieldRelations,
500                         fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getCodeName(),
501                                 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getForwardDisplayName(),
502                                 fieldGroup.getEntityIdentity()+"-"+iEntityRelations2Copy.getReverseDisplayName()));
503             }
504         
505         //Collect reference-relations that are from the composite to the field group's base entity descriptor
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             // Only add relation if all relation fields are present among the
527             // field group's wrapped fields.
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             //make descisions wether 2 add or not
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         // call recursively
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                     //check that the id field is the only id field in the base entity descriptor behind.
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                     //obs the display names
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         //call recursively
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     //Help methods -------------------------------------------------------------
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         // primary key + all required + creatable => creatable
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         //skip root field group
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                 //int xy = this.calculateFieldDescriptorFlags(field);
707                 int oldFlags = field.mFlags;
708                 int newFlags = oldFlags;
709                 if (field.isIdentityField())
710                 {
711                     //newFlags = oldFlags & ~ ( IFieldDescriptor.IDENTITY_FIELD | IFieldDescriptor.REQUIRED );
712                     //we don't want to have foreign id fields editable that could case saving something you don't realise you have edit
713                     newFlags = ( oldFlags & ~ ( IFieldDescriptor.IDENTITY_FIELD | IFieldDescriptor.REQUIRED ) ) | IFieldDescriptor.READ_ONLY_FIELD | IFieldDescriptor.AUTOGEN;
714                 }
715                 else
716                     //newFlags = (oldFlags | IFieldDescriptor.READ_ONLY_FIELD) & ~IFieldDescriptor.REQUIRED;
717                     newFlags = oldFlags & ~IFieldDescriptor.REQUIRED;
718                 field.mFlags = newFlags;
719                 //xy = this.calculateFieldDescriptorFlags(field);
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         //zero or more of these are allowed
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         // only one of these are allowed
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     //protected static int calculateFieldDescriptorFlags(IFieldDescriptor field)
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     // D E B U G   M E T H O D S    --------------------------------------------
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     // Superclass overrides ----------------------------------------------------
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         // Create entity.
858         IEntity entity = new CustomEntity(this);
859             
860         // Set default data.
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         //Create entity.
873         IEntity entity = new CustomEntity(this);
874         
875         //Load entity with data from the propertySource.
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     // ICompositeEntityDescriptor implementation -------------------------------
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     // Internal classes --------------------------------------------------------
970     
971     //nno-todo add methods get methods for field group's so adding of fields
972     // and new field groups can be made to an existing tree
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         // Data members --------------------------------------------------------
981         
982         /*** Stack for fields group's. */
983         private Stack mFieldGroup_Stack;
984         
985         // Constructors --------------------------------------------------------
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         // Access methods ------------------------------------------------------
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             //nno-todo test and check what happens in the composite case
1064             if (parentToChildPath == null)//calculate a new one automagically
1065             {
1066                 //obs! the direction of the path i.e. from the parent to the child
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             // OBS: Calling like this avoid problems with flag calculation
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             // extra closing's do nada
1109             if(mFieldGroup_Stack.size()<=1) 
1110                 return;
1111             
1112             //climb up one level in the tree
1113             CompositeFieldGroup currentFieldGroup = (CompositeFieldGroup)mFieldGroup_Stack.pop();
1114             
1115             //throw exception if group is empty! (Could have been: just remove the group.)
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             //make a copy and use that when constructing the new CompositeEntityDescriptor
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         // Help methods --------------------------------------------------------
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                 //make a copy of the internal structure and reset members
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     }//FieldGroupBuilder
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         // Data members --------------------------------------------------------
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         //null as long as this field group doesn't "belong" to a "wrapping" CompositeEntityDescriptor;
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;// only null for a root!
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             //set fields in this new root field group
1260             this(rootFieldGroup.getBaseEntityDescriptor(), rootFieldGroup.getEntityIdentity(), null, rootRequired);
1261             
1262             // hold on to the group that the child should be added to
1263             ICompositeFieldGroup virtualParent = nodeFieldGroup!=null?nodeFieldGroup:rootFieldGroup;
1264             CompositeFieldGroup realParent = null;
1265             
1266             //traverse and copy tree
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                 //copy fields and data BUT UNWRAP FIRST!!!
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                 //Copy field groups
1292                 // The order is irrelevant as long as we take each field group
1293                 // exactly ONCE and copy all it's child field groups. So either
1294                 //for (int j=iCompositeFieldGroup2Copy.getChildFieldGroupCount()-1;j>-1;j--)//breadfirst
1295                 // OR
1296                 for (int j=0;j<iCompositeFieldGroup2Copy.getChildFieldGroupCount();j++)//deapfirst
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                     //add group
1310                     compositeFieldGroupInNewTree.addChildFieldGroup(this, cfg, true);
1311                     //put them on the two stack's
1312                     stackForCopiedTree.push(icfg);
1313                     stackFornewTree.push(cfg);
1314                 }
1315             }
1316             //merge via a one level recursive call
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         // Help methods --------------------------------------------------------
1327         
1328         /*** Resolve naming conflicts for the member variable: "mEntityIdentity".
1329          */
1330         private int resolveEntityIdentityConflicts(CompositeFieldGroup rootFieldGroup, boolean resolveEntityIdentityConflicts)
1331         {
1332             //Traverse tree and solve naming conflicts
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                     //iterate til uniq    
1345                     do
1346                         entityIdentity2Check = increaseStringEndingWithNumber(entityIdentity2Check);
1347                     while ( takenIDs.containsKey(entityIdentity2Check) );
1348                     
1349                     //change mEntityIdentity or throw an exception
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             //dbg-printing Traverse the tree and prints all field groups
1368             //System.out.println("\n\n");
1369             //groupIterator = rootFieldGroup.getFieldGroups();
1370             //while (groupIterator.hasNext())
1371             //    System.out.println((ICompositeFieldGroup)groupIterator.next());
1372             //System.out.println("\n\n");
1373 
1374             return changes;
1375         }
1376         
1377         // Help methods --------------------------------------------------------
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         // Access methods ------------------------------------------------------
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             //checkForHolder();//unnecessary called later
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 			//checkForHolder();//unnecessary called later
1500             
1501 			int flags;
1502 			// OBS due to the ICompositeFieldDescriptor.isReadOnly()-method we
1503 			//always have to calculate the flags from the underlying source-field
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             // Unwrap field if neccessary.
1537             if (field instanceof ICompositeFieldDescriptor)
1538                 field = ((ICompositeFieldDescriptor)field).getSourceFieldDescriptor();
1539             
1540             // Validate the field descriptor.
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             // Wrap field and add it.
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             //Validate the RelationPath via getParentRelationPath()
1565 //            if(     fieldGroup.getParentRelationPath()==null ||
1566 //                    !( ( fieldGroup.getParentRelationPath().getFirstEntityDescriptor()==this.getBaseEntityDescriptor() &&
1567 //                         fieldGroup.getParentRelationPath().getLastEntityDescriptor()==fieldGroup.getBaseEntityDescriptor() ) ||
1568 //                       ( fieldGroup.getParentRelationPath().getFirstEntityDescriptor()==fieldGroup.getBaseEntityDescriptor() &&
1569 //                         fieldGroup.getParentRelationPath().getLastEntityDescriptor()==this.getBaseEntityDescriptor() )
1570 //                      ) ) 
1571 //                throw new InvalidRelationException("Relation path must link parent and child entities!");
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             //Resolve entity identity i.e. solve naming conflicts
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         // ICompositeFieldGroup implementation ---------------------------------
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         //nno-todo whoops have to take care of use of calculated field descriptors.
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         // super class overrides -----------------------------------------------
1671         
1672         public String toString()
1673         {
1674             return "FieldGroup-"+this.getEntityIdentity();
1675         }
1676         
1677         
1678         
1679     
1680         // Internal classes ----------------------------------------------------
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             // Data members ----------------------------------------------------
1689             private IFieldDescriptor mRemoteFieldDescriptor;
1690             private String mCodeName;
1691             private String mDisplayName;
1692             private int mFlags;
1693             
1694             // Constructors ----------------------------------------------------
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             // IFieldDescriptor implementation ---------------------------------
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                 //nno-todo Not nice but have to do for now. Solves problem
1817                 //when the data source descriptor hasn't been initialized!
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                 //nno-todo what about valiadators?
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                 // Perform base validations.
1864                 result = this.performBaseValidation(data);
1865     
1866 //                // Check validity using registered validators.
1867 //                for(int j=0; result.isValid() && mValidatorList!=null && j<mValidatorList.size(); j++)
1868 //                    if(!((IDataValidator)mValidatorList.get(j)).isDataValid(data, dataProvider))
1869 //                        result = new ValidationResult(this, ((IDataValidator)mValidatorList.get(j)).getInvalidMessage(data, dataProvider));
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                 // Validate null value.
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                 // Validate length.
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             // ICompositeFieldDescriptor implementation ------------------------
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             //Nested classes ---------------------------------------------------
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                         // Scan all fields in the current entity relation.
1996                         while(mNextObject==null && mFieldRelationIndex<getEntityDescriptor().getEntityRelation(mEntityRelationIndex).getFieldCount())
1997                         {
1998                             // Check if field is part of the field relation
1999                             IFieldRelation rel = getEntityDescriptor().getEntityRelation(mEntityRelationIndex).getFieldRelation(mFieldRelationIndex);
2000                             if(rel.getReferenceField()==CompositeFieldDescriptor.this || rel.getTargetField()==CompositeFieldDescriptor.this)
2001                                 mNextObject = rel;
2002                             
2003                             // Advance to the next field relation.
2004                             mFieldRelationIndex++;
2005                         }
2006                         
2007                         // Advance to the next entity relation.
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             }//FieldRelationIterator
2028             
2029             
2030         }//CompositeFieldDescriptor
2031     
2032     }//CompositeFieldGroup
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         // Data members ------------------------------------------------------------
2058         private IEntityRelation mEntityRelation;
2059         
2060 //        private transient IFieldDescriptor mReferenceField;
2061 //        private transient IFieldDescriptor mTargetField;
2062         private IFieldDescriptor mReferenceField;
2063         private IFieldDescriptor mTargetField;
2064         
2065         // Constructors ------------------------------------------------------------
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         // Superclass overrides ----------------------------------------------------
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         // IFieldRelation implementation -------------------------------------------
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         // Help methods ------------------------------------------------------------
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         // Constructors --------------------------------------------------------
2138         public CompositeStoreEntityAction(ICompositeEntityDescriptor entityDescriptor)
2139         {
2140         	super(entityDescriptor, STORE_ACTION, "Store", null);      
2141         }
2142         
2143         
2144         // Superclass overrides -----------------------------------------------
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             //transaction.addStore(iEntity);
2158             //that code-line from AbstractEntityDescriptor.java is replaced with:
2159             
2160             //validate descriptor
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             //update all dirty field groups or insert the root group
2168             if(iEntity.isPersistent())
2169             {   //executeUpdate
2170                 
2171                 //iterate over all field groups searching for dirty fields
2172                 for (Iterator groupIterator = iCompositeEntityDescriptor.getRootFieldGroup().getFieldGroups(); groupIterator.hasNext();)
2173                 {
2174                     ICompositeEntityDescriptor.ICompositeFieldGroup group = (ICompositeEntityDescriptor.ICompositeFieldGroup) groupIterator.next();
2175                     
2176                     //tmp variable used for checking and copying (and for shortening code-lines)!
2177                     ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor;
2178                                 
2179                     //check if there is a dirty field in the iterated group
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                     {//store data for this field group
2191                         
2192                         //get a primary-key for the iterated group
2193                         CompositeQualifier key = getKeyForFieldGroup(group, iEntity);
2194                         
2195                         //load the base entity used for storing
2196                         IEntity baseEntity = group.getBaseEntityDescriptor().loadEntity(key);
2197                                 
2198                         //copy data from the composite-entity -> the loaded base entity
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                         //AND FINALLY ADD THE BASE ENTITY CONTAINING COPIED DATA 2 TRANSACTION
2208                         transaction.addStore(baseEntity);
2209                     
2210                     }
2211                 }
2212             }
2213             else
2214             {//executeInsert
2215                 
2216                 //Only insert entity for the root field group
2217                 ICompositeEntityDescriptor.ICompositeFieldGroup rootGroup = iCompositeEntityDescriptor.getRootFieldGroup();
2218                                     
2219                 //create the base entity used 2 store
2220                 IEntity baseEntity = rootGroup.getBaseEntityDescriptor().createEntity();
2221                                             
2222                 //copy data from the composite-entity -> the created base entity
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                 //AND FINALLY ADD THE BASE ENTITY CONTAINING COPIED DATA 2 TRANSACTION
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             // validate descriptor
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                 //copy over IdentityField-data from the the created base entity -> composite-entity
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             //Reload the entity and copy over data 2 get refreshing!
2269             
2270 //            // get a primary-key for the root group
2271 //            //CompositeQualifier key = getKeyForFieldGroup(iCompositeEntityDescriptor.getRootFieldGroup(), iEntity);          
2272 //            CompositeQualifier key = new CompositeQualifier();
2273 //            for(int i=0;i<compositeEntityDescriptor.getRootFieldGroup().getFieldCount();i++)
2274 //            {
2275 //               ICompositeEntityDescriptor.ICompositeFieldDescriptor compositeFieldDescriptor;
2276 //               compositeFieldDescriptor = (ICompositeEntityDescriptor.ICompositeFieldDescriptor)compositeEntityDescriptor.getRootFieldGroup().getFieldDescriptor(i);
2277 //               if( compositeFieldDescriptor.isIdentityField() )
2278 //               {
2279 //                   key.add(new RelationQualifier(compositeFieldDescriptor, entity.getData(compositeFieldDescriptor)));
2280 //                   break;
2281 //               }
2282 //            }        
2283 //            IDataService iDataService = iCompositeEntityDescriptor.getDataSourceDescriptor().getDefaultDataSource().getDataService();
2284 //            if (iDataService instanceof CachedDataService)
2285 //                iDataService = ((CachedDataService)iDataService).getSourceDataService();
2286 //            IEntity tmpIEntity = iDataService.load(iCompositeEntityDescriptor, key);
2287 //            
2288 //            //copy over all data
2289 //            for (int i = 0; i < iEntity.getEntityDescriptor().getFieldCount(); i++)
2290 //            {
2291 //                iEntity.setData(iEntity.getEntityDescriptor().getFieldDescriptor(i), tmpIEntity.getData(tmpIEntity.getEntityDescriptor().getFieldDescriptor(i)));
2292 //            }
2293             
2294             entity.refresh();
2295             
2296             entity.setStatusFlag(IEntity.PERSISTENT);
2297             entity.clearStatusFlag(IEntity.DIRTY | IEntity.EMPTY);
2298             
2299 //            //reload sub-entities to make changes appear in them
2300 //            Iterator groupIterator = compositeEntityDescriptor.getRootFieldGroup().getFieldGroups();
2301 //            while (groupIterator.hasNext())
2302 //            {
2303 //                ICompositeFieldGroup fieldGroup = (ICompositeFieldGroup) groupIterator.next();
2304 //                CompositeQualifier key = getKeyForFieldGroup(fieldGroup, entity);
2305 //                //IEntity subEntity = fieldGroup.getBaseEntityDescriptor().loadEntity(key);
2306 //                
2307 //                IDataService dataService = compositeEntityDescriptor.getDataSourceDescriptor().getDefaultDataSource().getDataService();
2308 //                if (dataService instanceof CachedDataService )
2309 //                    dataService = ((CachedDataService)dataService).getSourceDataService();
2310 //                IEntity tmpIEntity = dataService.load(fieldGroup.getBaseEntityDescriptor(), key);
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 //      IUnaryEntityAction implementation -----------------------------------
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         // Help methods --------------------------------------------------------
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     }//CompositeStoreEntityAction
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         // Constructors --------------------------------------------------------
2353         public CompositeDeleteEntityAction(ICompositeEntityDescriptor entityDescriptor)
2354         {
2355             super(entityDescriptor, DELETE_ACTION, "Delete", null);
2356         }
2357 
2358         // Superclass overrides -----------------------------------------------
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                 //transaction.addDelete(iEntity);
2370                 //that code-line from AbstractEntityDescriptor.java is replaced with:
2371     
2372                 //validate descriptor
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                 //create an primary-key for the iterated group
2382                 CompositeQualifier key = getKeyForFieldGroup(rootGroup, iEntity);
2383                     
2384                 //load the base entity to delete
2385                 IEntity baseEntity = rootGroup.getBaseEntityDescriptor().loadEntity(key);
2386                 
2387                 //AND FINALLY ADD THE BASE ENTITY TO BE DELETED TO THE TRANSACTION
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         // IUnaryEntityAction implementation -----------------------------------
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     }//CompositeDeleteEntityAction
2422     
2423     
2424     public static CompositeQualifier getKeyForFieldGroup(
2425                             ICompositeEntityDescriptor.ICompositeFieldGroup group, 
2426                             IEntity iEntity)
2427     {
2428         // create an primary-key for the field group
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 }//CompositeEntityDescriptor
2446 
2447