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  package org.caleigo.core;
19  
20  
21  import java.util.*;
22  
23  import org.caleigo.core.exception.*;
24  import org.caleigo.toolkit.log.*;
25  import org.caleigo.toolkit.util.*;
26  
27  /*** <Description of AbstractEntityDescriptor>
28  *
29  * @author Dennis Zikovic
30  * @version 1.00
31  * 
32  *//* 
33  *
34  * WHEN        WHO		WHY & WHAT
35  * ------------------------------------------------------------------------------
36  * 2001-03-12  Dennis Zikovic    Creation
37  * 2003-07-03  Niklas Norberg    Changed method createFieldDescriptor(int index, String codeName, -> createFieldDescriptor(String codeName,
38  *                               Added createCalculatedFieldDescriptors()
39  *                               Added getDataFieldCount()
40  */
41  public abstract class AbstractEntityDescriptor implements IEntityDescriptor
42  {
43      // Constants ---------------------------------------------------------------
44      public static final int SELECTABLE = 0x0F; // Count <20
45      public static final int LISTABLE = 0x0E; // Count <100
46      public static final int CACHEABLE = 0x0C; // Count <1000
47      public static final int SCANABLE = 0x08; // Count <1000000, else avoid table scan.
48      
49      public static final int CREATABLE = 0x10; 
50      public static final int EDITABLE = 0x20; 
51      public static final int DELETABLE = 0x40; 
52      
53      // Data members ------------------------------------------------------------
54      private String mCodeName;
55      private String mSourceName;
56      private String mDisplayName;
57      private String mEntityClassName;
58      private String mDataSourceClassName;
59      private int mEntityType;
60      
61      private int mFlags;
62      private int mCacheTime;
63      
64      private IFieldDescriptor[] mFieldDescriptorList;
65      private int mDataFieldCount;
66      private IEntityRelation[] mEntityRelationList;
67      
68      private List mEntityActionList;
69      
70      private EntityCollator mNaturalOrderCollator;
71  
72      // Constructors ------------------------------------------------------------
73      
74      /*** Note that the constructor for this class is protected.
75       */ 
76      protected AbstractEntityDescriptor(
77              String codeName, 
78              String sourceName, 
79              String displayName, 
80              String entityClassName, 
81              String dataSourceClassName, 
82              int entityType, 
83              IFieldDescriptor[] fields,
84              IEntityRelation[] relations)
85      {
86          // Member data initializations.
87          mCodeName = codeName;
88          mSourceName = sourceName;
89          mDisplayName = displayName;
90          mEntityClassName = entityClassName;
91          mDataSourceClassName = dataSourceClassName;
92          mEntityType = entityType;
93          mDataFieldCount = fields.length;       
94          mEntityRelationList = relations;
95          mFlags = SCANABLE;
96          mCacheTime = 0;
97          
98          // Extends mFieldDescriptorList with calculated fields if such exists.
99          ICalculatedFieldDescriptor[] calculatedFieldDescriptorList = createCalculatedFieldDescriptors();
100         if (calculatedFieldDescriptorList == null)
101         {
102             mFieldDescriptorList = fields; 
103         }
104         else
105         {
106             mFieldDescriptorList = new IFieldDescriptor[fields.length + calculatedFieldDescriptorList.length];
107             System.arraycopy( fields, 0, mFieldDescriptorList, 0, fields.length );
108             System.arraycopy( calculatedFieldDescriptorList, 0, mFieldDescriptorList, fields.length, calculatedFieldDescriptorList.length );
109         }
110          
111         // Action initializations.
112         mEntityActionList = new ArrayList(5);
113         this.addEntityAction(this.createStoreAction());
114         this.addEntityAction(this.createDeleteAction());
115         this.defineEntityActions();      
116     }
117 
118      
119 	
120     // Superclass overrides ----------------------------------------------------
121     public String toString()
122     {
123         return this.getDataSourceDescriptor().getCodeName()+"."+this.getCodeName();
124     }
125 
126     protected Object writeReplace() throws java.io.ObjectStreamException
127     {
128         return new Dezerializer(this.getClass().getName());
129     }
130     
131     // Action methods ----------------------------------------------------------
132     
133     /*** Creates an entity with of the type described by this descriptor and
134      * loads it with default data.
135      */
136     public IEntity createEntity()
137     {
138         IEntity entity = null;
139         try
140         {
141             // Create entity.
142             entity = (IEntity)this.getEntityClass().newInstance();
143         }
144         catch(Exception e)
145         {
146         }
147         return entity;
148     }
149     
150     /*** Creates an entity with of the type described by this descriptor and
151      * loads it with data from the provided property source.
152      */
153     public IEntity createEntity(IDataProvider propertySource)
154     {
155         IEntity entity = null;
156         try
157         {
158             // Create entity.
159             entity = (IEntity)this.getEntityClass().newInstance();
160             
161             // Load entity with data from the propertySource.
162             entity.setData(propertySource);
163         }
164         catch(Exception e)
165         {
166         }
167         return entity;
168     }
169     
170     /*** Loads a selection of entities of the called descriptors type using
171      * the provided entity descriptor. The method uses the default data source
172      * for the entity type.
173      */
174     public ISelection loadSelection(Qualifier qualifier) 
175     {
176         // Verify qualifier.
177         if(qualifier!=null && !qualifier.canQualify(this))
178             throw new InvalidQualifierException("Qualifier can not qualifiy "+this+", qualifier was: "+qualifier);
179         
180         // Verify existance of a default data source.
181         if(this.getDataSourceDescriptor().getDefaultDataSource()==null)
182             throw new DataServiceNotFoundException("No default data source has been set for: "+this.getDataSourceDescriptor().getCodeName());
183             
184         // Perform load via the default data source.
185         return this.getDataSourceDescriptor().getDefaultDataSource().loadSelection(this, qualifier);
186     }
187     
188     /*** The load method returnes a single entity object intentified by the 
189      * provided indentity qualifier that must be able to uniquely identify
190      * a single entity instance.
191      */
192     public IEntity loadEntity(Qualifier identityQualifier)
193     {
194         // Verify qualifier.
195         if(identityQualifier!=null && !identityQualifier.canUniquelyQualify(this))
196             throw new InvalidQualifierException("Qualifier can not uniquely qualifiy "+this);
197         
198         // Verify existance of a default data source.
199         if(this.getDataSourceDescriptor().getDefaultDataSource()==null)
200             throw new DataServiceNotFoundException("No default data source has been set for: "+this.getDataSourceDescriptor().getCodeName());
201             
202         // Perform load via the default data source.
203         return this.getDataSourceDescriptor().getDefaultDataSource().loadEntity(this, identityQualifier);
204     }
205     
206     // Access methods ----------------------------------------------------------
207     
208     /*** Returns an identifying name that can be used to address the field in
209      * client software if the field descriptor for some reason is not available.
210      */
211     public String getCodeName()
212     {
213         return mCodeName;
214     }
215     
216     /*** Returns an identifying name used in communication with the data service
217      * layer. Should normally never be used by the client developer.
218      */ 
219     public String getSourceName()
220     {
221         return mSourceName;
222     }
223     
224     /*** Returns the class for the entities described by the entity descriptor.
225      */
226     public String getDisplayName()
227     {
228         return ResourceProvider.getString(mCodeName, mCodeName, mDisplayName);
229     }
230     
231     /*** Returns the class for the entities described by the entity descriptor.
232      */
233     public Class getEntityClass()
234     {
235         try
236         {
237             return Class.forName(mEntityClassName);
238         }
239         catch(Exception e)
240         {
241             // This is an invalid state. Throw exception?
242             return null;
243         }
244     }
245     
246     /*** Returns the entity type signified by the constants... <p> 
247      * MASTER_ENTITY = Signifies that these enitities are handled as seperate 
248      * units that can be viewed and handled as isolated units. <BR>
249      * SLAVE_ENTITY = These entities are generally never viewed or handled as
250      * an isolated seperate unit. They are genneraly always viewed and 
251      * manipulated as slaves or children to another entity type that generaly
252      * is a master entity. <BR>
253      * LINK_ENTITY = Signifies that the purpose of this entity is only to link 
254      * other entity types together in N to N relationships. <BR>
255      * STATIC_ENTITY = Used for tables storing system constants that rarely or
256      * never changes. These entities are generally cacheable with no time limit. <BR>
257      * CUSTOM_ENTITY = Used for entities that are composites of other entities 
258      * and therefore can not specify another entity type. 
259      */
260     public int getEntityType()
261     {
262         return mEntityType;
263     }
264 	
265     /*** Returns true if entities described by the called descriptor may be 
266      * created. If a store operation is called on a non-persistent entity
267      * of the described type an exception should be thrown.
268      */
269     public boolean isCreatable()
270     {
271         return (mFlags & CREATABLE)==CREATABLE && !this.getDataSourceDescriptor().isReadOnly();
272     }
273     
274     /*** Returns true if entities described by the called descriptor may be 
275      * updated. If a store operation is called on a persistent entity
276      * of the described type an exception should be thrown.
277      */
278     public boolean isEditable()
279     {
280         return (mFlags & EDITABLE)==EDITABLE && !this.getDataSourceDescriptor().isReadOnly();
281     }
282     
283     /*** Returns true if entities described by the called descriptor may be 
284      * deleted. If a store operation is called on a persistent entity
285      * of the described type an exception should be thrown.
286      */
287     public boolean isDeletable()
288     {
289         return (mFlags & DELETABLE)==DELETABLE && !this.getDataSourceDescriptor().isReadOnly();
290     }
291     
292     /*** Returns the acceptable time in seconds to cache entities described
293      * by the descriptor. Zero means no caching is accepable and the constant 
294      * NO_TIME_LIMIT sinifies that there are no time limit when cacheing.
295      */
296     public int getCacheTime()
297     {
298         return mCacheTime;
299     }
300     
301     /*** This routine should return true if it possible to view and select 
302      * between ALL existing entities. In other words if it possible to use 
303      * for instance a combo box to select between the entities. 
304      * This is generally true when the entity count is less then 20.
305      */  
306     public boolean isSelectable()
307     {
308         return (mFlags & SELECTABLE)==SELECTABLE;
309     }
310     
311     /*** This routine should return true if it is meaningfull to display ALL 
312      * existing entities for a user at the same time. This is generally true
313      * when the entity count is less then 200.
314      */  
315     public boolean isListable()
316     {
317         return (mFlags & LISTABLE)==LISTABLE;
318     }
319     
320     /*** This routine should return true if it "pays" for the system to cache 
321      * the entire table when accessing a single entity. This depends on many 
322      * factors, the size of the entities, the bandwith and the frequence of
323      * entity access. If the amount of entities exceds 2000 they generaly not
324      * considered cachable.
325      */  
326     public boolean isCacheable()
327     {
328         return (mFlags & CACHEABLE)==CACHEABLE;
329     }
330     
331     /*** This routine should return true if it is acceptable for the system 
332      * to for any reason scan all existing entities of this type. In other 
333      * words if a tablescan is acceptable or should be avoide. If the amount
334      * of entities excedes a million this is generally not true.
335      */ 
336     public boolean isScanable()
337     {
338         return (mFlags & SCANABLE)==SCANABLE;
339     }
340 	
341     /*** Returns the number of field descriptors that this entity descriptor
342      * contains. This includes both data and calculated field descriptors.
343      */
344     public int getFieldCount()
345     {
346         return mFieldDescriptorList.length ;
347     }
348     
349    /*** Returns the number of field descriptors that have persistent data or in 
350      * other words are not descendants to ICalculatedFieldDescriptor's that this
351      * entity descriptor contains.
352      */
353     public int getDataFieldCount()
354     {
355         return mDataFieldCount;
356     }
357    
358     /*** Returns a specific field descriptor from the entity descriptor addressed
359      * by its ordial index. May cast OutOfBoundException.
360      */
361     public IFieldDescriptor getFieldDescriptor(int index)
362     {
363         return mFieldDescriptorList[index];
364     }
365 
366     /*** Returns a specific field descriptor from the entity descriptor addressed
367      * by its code name. May return null if the codeName was not identified.
368      */
369     public IFieldDescriptor getFieldDescriptor(String codeName)
370     {
371         int index = this.getFieldIndex(codeName);
372         if(index>=0)
373             return mFieldDescriptorList[index];
374         else
375             return null;
376     }
377 
378     /*** Returns all field descriptors as an iterator.
379      */
380     public java.util.Iterator getFieldDescriptors()
381     {
382         return Iterators.iterate(mFieldDescriptorList);
383     }
384         
385     /*** Returns the DataSource descriptor that this entity is a part of.
386      */
387     public IDataSourceDescriptor getDataSourceDescriptor()
388     {
389         IDataSourceDescriptor dataSourceDescriptor = null;
390         try
391         {
392             Class sourceClass = Class.forName(mDataSourceClassName);
393             java.lang.reflect.Field field = sourceClass.getField("instance");
394             dataSourceDescriptor = (IDataSourceDescriptor)field.get(null);
395         }
396         catch(Exception e)
397         {
398             String msg = "Invalid data source descriptor data: "+mDataSourceClassName;
399             Log.printError(this, msg);
400             throw new InvalidDescriptorException(msg, e);
401         }
402         return dataSourceDescriptor;
403     }
404 
405     /*** Reurns true if the provided field descriptor is a part the called 
406      * entity descriptor.
407      */
408     public boolean contains(IFieldDescriptor fieldDescriptor)
409     {
410         return this.getFieldIndex(fieldDescriptor)>=0;
411     }
412     
413     /*** Returns the index of the provided field descriptor or a negative value
414      * if the field is not a part of the entity descriptor.
415      */
416     public int getFieldIndex(IFieldDescriptor fieldDescriptor)
417     {
418         for(int j=0; j<this.getFieldCount(); j++)
419             if(this.getFieldDescriptor(j)==fieldDescriptor)
420                 return j;
421         return -1;    
422     }
423     
424     /*** Returns the index of the field descriptor with the provided code name 
425      * or a negative value if the field is not a part of the entity descriptor.
426      */
427     public int getFieldIndex(String codeName)
428     {
429         for(int j=0; j<this.getFieldCount(); j++)
430             if(this.getFieldDescriptor(j).getCodeName().equals(codeName))
431                 return j;
432         return -1;
433     }
434     
435     /*** Access method that returns the number of IEntityRelation:s that has 
436      * the called entity descriptor as source or target. 
437      */ 
438     public int getEntityRelationCount()
439     {
440         return mEntityRelationList!=null ? mEntityRelationList.length : 0;
441     }
442     
443     /*** Access method that returns the indexed IEntityRelation.
444      */
445     public IEntityRelation getEntityRelation(int index)
446     {
447         return mEntityRelationList[index];
448     }
449         
450     /*** Returns the index of the IEntityRelation with the specified code name, or
451      * <code>-1</code> if no relation is found.
452      */
453     public int getEntityRelationIndex(String codeName)
454     {
455         for(int j=0; j<this.getEntityRelationCount(); j++)
456             if(this.getEntityRelation(j).getCodeName().equals(codeName))
457                 return j;
458         return -1;
459     }
460     
461     /*** Access method that returns an editable copy of the IEntityRelation list
462      * with all relation objects that the descriptor is part of.
463      */
464     public Iterator getEntityRelations()
465     {
466         return Iterators.iterate(mEntityRelationList); 
467     }
468 
469     /*** Access method that returns the number of IEntityAction objects that are 
470      * available for access from the called IEntityDescriptor.
471      */
472     public int getActionCount()
473     {
474         return mEntityActionList.size();
475     }
476     
477     /*** Returns the indexed IEntityAction.
478      */
479     public IEntityAction getAction(int index)
480     {
481         return (IEntityAction)mEntityActionList.get(index);
482     }
483     
484     /*** Returns the IEntityAction with the provided code name. Returns null if
485      * the code name could not be identified.
486      */
487     public IEntityAction getAction(String codeName)
488     {
489         IEntityAction action = null;
490         for(int j=0; action==null && j<this.getActionCount(); j++)
491             if(this.getAction(j).getCodeName().equals(codeName))
492                 action = this.getAction(j);
493         return action;
494     }
495     
496     /*** Returns an iterator with all contained IEntityAction objects that are  
497      * available for access from the called IEntityDescriptor.
498      */
499     public java.util.Iterator getActions()
500     {
501         return Iterators.unmodifiableIterator(mEntityActionList.iterator());
502     }
503 
504     /*** Returns en entity collator defining the natural order for the called
505      * entity descriptors entities. 
506      */
507     public EntityCollator getNaturalOrder()
508     {
509         if(mNaturalOrderCollator==null)
510         {
511             IFieldDescriptor orderField = null;
512             
513             // Checkfor natural order field.
514             for(int j=0; orderField==null && j<this.getFieldCount(); j++)
515                 if(this.getFieldDescriptor(j).isNaturalOrder())
516                     orderField = this.getFieldDescriptor(j);
517             
518             // If no natural order field found use first identity field.
519             for(int j=0; orderField==null && j<this.getFieldCount(); j++)
520                 if(this.getFieldDescriptor(j).isIdentityField())
521                     orderField = this.getFieldDescriptor(j);
522                     
523             // If no field found then use first field in entity.
524             if(orderField==null)
525                 orderField = this.getFieldDescriptor(0);
526                     
527             mNaturalOrderCollator = new EntityCollator(this);
528             mNaturalOrderCollator.addCollationField(orderField);
529         }
530         return mNaturalOrderCollator;
531     }
532     
533     // Help methods ------------------------------------------------------------
534     
535     /*** Sets a optional cache time for the entity. A negative value means
536      * that entities can be cached forever.
537      */
538     protected void setCacheTime(int seconds)
539     {
540         mCacheTime = seconds;
541     }
542     
543     /*** Sets the flags for the entity descriptor. Valid values are SELECTABLE, 
544      * LISTABLE, CACHEABLE, SCANABLE, CREATABLE, EDITABLE, DELETABLE or 
545      * a combination of these.
546      */
547     protected void setFlags(int cacheFlags)
548     {
549         mFlags = cacheFlags;
550     }
551     
552     /*** Registers an new IEntityAction object  as part of the entity descriptor.
553      * These actions can then be accessed and be reflected using the entity
554      * descriptor. By declaring the propper flags it will be automatically 
555      * integrated in the applications user interface.
556      * 
557      * @param entityAction The IEntityAction to be added.
558      */
559     protected void addEntityAction(IEntityAction entityAction)
560     {
561         if(entityAction.getEntityDescriptor()!=this)
562             throw new InvalidDescriptorException("Invalid entity descriptor used by action object!");
563         mEntityActionList.add(entityAction);
564     }
565     
566     /*** Creates and returns the store action for entity objects of the type
567      * described by the called entity descriptor. The method can be used to 
568      * return an modified store action that for example could be used to store 
569      * additional auditing information or modify the data to be stored.
570      * 
571      * @return The IEntityAction object that should be used to store entity
572      *          objects for the called entity. By default the nested 
573      *          StoreEntityAction class will be returned.
574      */
575     protected IEntityAction createStoreAction()
576     {
577         return new StoreEntityAction(this);
578     }
579         
580     /*** Creates and returns the delete action for entity objects of the type
581      * described by the called entity descriptor. The method can be used to 
582      * return an modified delete action that for example could be used to 
583      * backup the deleted data to another format.
584      * 
585      * @return The IEntityAction object that should be used to delete entity
586      *          objects for the called entity. By default the nested 
587      *          DeleteEntityAction class will be returned.
588      */
589      protected IEntityAction createDeleteAction()
590     {
591         return new DeleteEntityAction(this);
592     }
593         
594     /*** Called by the constructor. The method is expected to add all the 
595      * descriptors entity action objects.
596      */
597     protected void defineEntityActions()
598     {
599     }
600     
601     /*** This help method makes possible for sub classes in other packages to 
602      * create field descriptor. Note that this is the only way to make instances 
603      * if you dont wont sub class the field descriptor.
604      */ 
605     protected static IFieldDescriptor createFieldDescriptor(String codeName, String sourceName, String displayName, String entityDescriptorName, DataType dataType, int length, int flags, Object defValue)
606     {
607         return new FieldDescriptor(codeName, sourceName, displayName, entityDescriptorName, dataType, length, flags, defValue);
608     }
609     
610     /*** This should be overridden if one want to add ICalculatedFieldDescriptor's
611      * to an EntityDescriptor. Returns null by default witch means that no
612      * calcultaed fields are added.
613      */
614     protected ICalculatedFieldDescriptor[] createCalculatedFieldDescriptors()
615     {
616         return null;
617     }
618  
619     /*** This help method makes possible for sub classes in other packages to 
620      * easily create field relations. 
621      */ 
622     protected static IFieldRelation createFieldRelation(String sourceEntityDescriptorClassName, String sourceFieldDescriptorCodeName, String targetEntityDescriptorClassName, String targetFieldDescriptorCodeName)
623     {
624         return new FieldRelation(sourceEntityDescriptorClassName, sourceFieldDescriptorCodeName, targetEntityDescriptorClassName, targetFieldDescriptorCodeName);
625     }
626     
627     /*** This help method makes possible for sub classes in other packages to 
628      * easily create field relations. 
629      */ 
630     protected static IEntityRelation createEntityRelation(String sourceEntityDescriptorClassName, String sourceFieldDescriptorCodeName, String targetEntityDescriptorClassName, String targetFieldDescriptorCodeName, String codeName, String forwardName, String reverseName)
631     {
632         return new EntityRelation(new FieldRelation(sourceEntityDescriptorClassName, sourceFieldDescriptorCodeName, targetEntityDescriptorClassName, targetFieldDescriptorCodeName), codeName, forwardName, reverseName);
633     }
634         
635     /*** This help method makes possible for sub classes in other packages to 
636      * easily create entity relations. 
637      */ 
638     protected static IEntityRelation createEntityRelation(IFieldRelation fieldRelation, String codeName, String forwardName, String reverseName)
639     {
640         return new EntityRelation(fieldRelation, codeName, forwardName, reverseName);
641     }
642         
643     /*** This help method makes possible for sub classes in other packages to 
644      * easily create entity relations. 
645      */ 
646     protected static IEntityRelation createEntityRelation(IFieldRelation[] fieldRelation, String codeName, String forwardName, String reverseName)
647     {
648         return new EntityRelation(fieldRelation, codeName, forwardName, reverseName);
649     }
650     
651     // TODO: Move getJNDIName(), getRemoteClass() and getHomeClass() methods!
652     public String getJNDIName()
653     {
654         return null;
655     }
656     
657     public Class getRemoteClass()
658     {
659         return null;
660     }
661     
662     public Class getHomeClass()
663     {
664         return null;
665     }
666         
667     // Nested classes ----------------------------------------------------------
668     protected static class Dezerializer implements java.io.Serializable
669     {
670         // Data members --------------------------------------------------------
671         protected String mClassName;
672         
673         // Constructors --------------------------------------------------------
674         public Dezerializer(String className)
675         {
676             mClassName = className;
677         }
678         
679         // Action methods ------------------------------------------------------
680         protected Object readResolve() throws java.io.ObjectStreamException
681         {
682             try
683             {
684                 Class entityDescriptorClass = Class.forName(mClassName);
685                 java.lang.reflect.Field instanceField = entityDescriptorClass.getField("instance");
686                 return instanceField.get(null);
687             }
688             catch (Exception e)
689             {
690                 Log.printError(this, "Error in readResolve", e);
691                 throw new java.io.InvalidObjectException(e.getMessage());
692             }
693         }
694     }
695     
696     /*** The default store action used by the AbstsractEntityDescriptor class.
697      */
698     public static class StoreEntityAction extends AbstractTransactionEntityAction implements IUnaryEntityAction
699     {
700         // Constructors --------------------------------------------------------
701         public StoreEntityAction(IEntityDescriptor entityDescriptor)
702         {
703             super(entityDescriptor, STORE_ACTION, "Store", null);
704         }
705 
706         // Superclasss overrides -----------------------------------------------
707 
708         /*** Prepare method for the action where the action is expected to add 
709          * the data operations needed to perform the action.
710          */
711         public void prepareTransaction(IDataBundle data, IDataTransaction transaction)
712         {
713             this.prepareEntityData((IEntity)data.getData(0));
714             this.validateData(data);
715             transaction.addStore((IEntity)data.getData(0));
716         }
717 
718         /*** This method is called by the action objects constructor once and only 
719          * once. The method is expected to define the action data requriments
720          * (IDataBundleDescriptor) using the diferent add methods for descriptors
721          * defined in this class.
722          */
723         public final void defineDataDescriptor()
724         {
725             this.addEntityDescriptor(this.getEntityDescriptor(), this.getEntityDescriptor().getCodeName(), this.getEntityDescriptor().getDisplayName(), null);
726         }
727     
728         // IUnaryEntityAction implementation -----------------------------------
729         public void perform(IEntity entity)
730         {
731             IDataBundle data = this.getDataBundleDescriptor().createActionData();
732             data.setData(0, entity);
733             this.perform(data);
734         }
735         
736         // Help methods --------------------------------------------------------
737         
738         /*** Simple help method that is called when an action are triggered 
739          * emmidiatelly before the action is validated. The method is 
740          * provided for optional override and does nothing by default.
741          */
742         protected void prepareEntityData(IEntity entity)
743         {
744         }
745     }
746     
747     /*** The default delete action used by the AbstsractEntityDescriptor class.
748      */
749     public static class DeleteEntityAction extends AbstractTransactionEntityAction implements IUnaryEntityAction
750     {
751         // Constructors --------------------------------------------------------
752         public DeleteEntityAction(IEntityDescriptor entityDescriptor)
753         {
754             super(entityDescriptor, DELETE_ACTION, "Delete", null);
755         }
756 
757         // Superclasss overrides -----------------------------------------------
758 
759         /*** Prepare method for the action where the action is expected to add 
760          * the data operations needed to perform the action.
761          */
762         public void prepareTransaction(IDataBundle data, IDataTransaction transaction)
763         {
764             if(((IEntity)data.getData(0)).isPersistent())
765                 transaction.addDelete((IEntity)data.getData(0));
766         }
767 
768         /*** This method is called by the action objects constructor once and only 
769          * once. The method is expected to define the action data requriments
770          * (IDataBundleDescriptor) using the diferent add methods for descriptors
771          * defined in this class.
772          */
773         public final void defineDataDescriptor()
774         {
775             this.addEntityDescriptor(this.getEntityDescriptor(), this.getEntityDescriptor().getCodeName(), this.getEntityDescriptor().getDisplayName(), null);
776         }
777     
778         // IUnaryEntityAction implementation -----------------------------------
779         public void perform(IEntity entity)
780         {
781             IDataBundle data = this.getDataBundleDescriptor().createActionData();
782             data.setData(0, entity);
783             this.perform(data);
784         }
785     }
786 }