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.*;
22  
23  import org.caleigo.core.event.*;
24  import org.caleigo.core.exception.*;
25  
26  
27  /*** The AbstractEntity class is an abtsract implementation of the IEntity
28   * interface. Subclasses only need to override the tree methods getRawData, 
29   * setRawData and getEntityDescriptor.
30   *
31   * Entities are data objects representing single instances of data rows/records 
32   * from a persistent data storage. An entity contains several fields of data 
33   * accessible using the setData() and getData() methods. 
34   *
35   * @author  Dennis Zikovic
36   * @version 1.00
37   * 
38   *//* 
39   *
40   * WHEN        WHO               WHY & WHAT
41   * ------------------------------------------------------------------------------
42   * 2001-07-08  Dennis Zikovic    Creation
43   */
44  public abstract class AbstractEntity implements IEntity 
45  {
46      // Data members ------------------------------------------------------------
47      private byte mStatusFlags;
48      private BitSet mDirtyFlags;
49      private Qualifier mOriginIdentityQualifier;
50      
51      private transient IEntityListener mEntityListener;
52      private transient IEntityChangeListener mEntityChangeListener;
53      
54      // Static methods ----------------------------------------------------------
55      
56      // Help methods ------------------------------------------------------------
57      
58      /*** Makes a descriptive log string for the provided entity object.
59       * Will only display the data from the identity fields. 
60       */
61      public static String makeLogString(IEntity entity)
62      {
63          return makeLogString(entity, null);
64      }
65      
66      /*** Makes a descriptive log string for the provided entity object. 
67       * Will only display the data for the provided field descriptors. 
68       */
69      public static String makeLogString(IEntity entity, IFieldDescriptor[] fields)
70      {
71          StringBuffer buf = new StringBuffer(100);
72          
73          // Add the entity's code name
74          buf.append(entity.getEntityDescriptor().getCodeName());
75          
76          // Add the defining identity fields.
77          buf.append('(');
78          if(fields==null)
79          {
80              boolean first = true;
81              for(int j=0; j<entity.getEntityDescriptor().getDataFieldCount(); j++)
82                  if(entity.getEntityDescriptor().getFieldDescriptor(j).isIdentityField())
83                  {
84                      if(!first)
85                          buf.append(", ");
86                      buf.append(entity.getEntityDescriptor().getFieldDescriptor(j).getDataType().makeLogString(entity.getData(entity.getEntityDescriptor().getFieldDescriptor(j))));
87                      first = false;
88                  }
89          }
90          else
91          {
92              for(int j=0; j<fields.length; j++)
93              {
94                  if(j!=0)
95                      buf.append(", ");
96                  buf.append(fields[j].getDataType().makeLogString(entity.getData(fields[j])));
97              }
98          }
99          buf.append(')');
100         
101         // Add the status flags.
102         buf.append(' ');
103         if(entity.isDirty())
104             buf.append('D');
105         else
106             buf.append('-');
107         if(entity.isEmpty())
108             buf.append('E');
109         else
110             buf.append('-');
111         if(entity.isPersistent())
112             buf.append('P');
113         else
114             buf.append('-');
115         
116         return buf.toString();
117     }
118     
119     // Constructors ------------------------------------------------------------
120     
121     /*** Default constructor for AbstractEntity.
122      */
123     public AbstractEntity() 
124     {
125         this.clear();
126     }
127     
128     public AbstractEntity(Qualifier identityQualifier) 
129     {
130         mStatusFlags = EMPTY;
131         
132         // Verify identity descriptor
133         if(identityQualifier==null || !identityQualifier.canUniquelyQualify(this.getEntityDescriptor()))
134             throw new InvalidQualifierException("Expected indentity qualifier for: "+this.getEntityDescriptor());
135         
136         // Load entity with data.
137         IDataService service = this.getDataSource().getDataService();
138         IDataTransaction trans = service.newTransaction();
139         trans.addLoad(identityQualifier, this);
140         trans.commit();
141     }
142     
143     // Abstract methods --------------------------------------------------------
144     
145     /*** Return the entity objects IEntityDescriptor that defines it's type and 
146      * structure. Enables extended means of reflection for the entity. 
147      */
148     public abstract IEntityDescriptor getEntityDescriptor();
149     
150     /*** Should return the contained indexed data avoidinng any convertions 
151      * or formating. Note the package scope used to avoid missuse.
152      */
153     abstract Object getRawData(int index);
154     
155     /*** Should set the indexed data avoiding any convertions or format changes.
156      * Must not update any flags or fire any event handled by the 
157      * AbstractEntityDescriptor. Note the package scope used to avoid missuse.
158      */
159     abstract void setRawData(int index, Object data);
160     
161     // Superclass overrides ----------------------------------------------------
162     
163     /*** Overriden to display the entity description type, identifying data and
164      * state of the status flags (D)IRTY, (E)MPTY and (P)ERSISTANT.
165      */
166     public String toString()
167     {
168         return makeLogString(this);
169     }
170     
171     // IEntity implementation --------------------------------------------------
172     
173     /*** This method will store any unsaved changes in the entity to it's 
174      * realated persistent storage. This will reset the DIRTY flag and set 
175      * the PERSISTENT flag.
176      */ 
177     public void store() 
178     {
179         IEntityAction action = this.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION);
180         if(action instanceof IUnaryEntityAction)
181             ((IUnaryEntityAction)action).perform(this);
182         else
183         {
184             IDataBundle data = action.getDataBundleDescriptor().createActionData();
185             data.setData(0, this);
186             action.perform(data);
187         }
188     }
189     
190     /*** This method will delete the entity from it's related persistent storage.
191      * This will set the DIRTY flag and reset the PERSISTENT flag. Note that 
192      * that the actual java object will not be deleted or changed in any other 
193      * way besides its status changes makeing possible to recreate the object
194      * by simply calling the store() method.
195      */
196     public void delete() 
197     {
198         IEntityAction action = this.getEntityDescriptor().getAction(IEntityDescriptor.DELETE_ACTION);
199         if(action instanceof IUnaryEntityAction)
200             ((IUnaryEntityAction)action).perform(this);
201         else
202         {
203             IDataBundle data = action.getDataBundleDescriptor().createActionData();
204             data.setData(0, this);
205             action.perform(data);
206         }
207     }
208     
209     /*** This method will refresh the entity with current data from the related 
210      * persistent storage. All contained data with any existing changes will
211      * be replaced from the storage. This will reset the DIRTY flag and can
212      * possibly reset the PERSISTENT flag if the entity can no longer be found 
213      * in the related database/storage. 
214      */
215     public void refresh() 
216     {
217         if(this.getDataSource()!=null)
218             this.getDataSource().getDataService().refresh(this);
219         else
220             throw new DataServiceNotFoundException("No default data service has been set for data source: "+this.getEntityDescriptor().getDataSourceDescriptor().getCodeName());
221     }
222         
223     /*** Copies and updates the in the entity containde data by reading each 
224      * individual data field as a property from the provided property source.
225      * Note that since IEntity extends the IDataProvider interface it is
226      * thereby possibli to copy other entities with this method. Even if the
227      * etities are not of the same type all fields with the same name will be 
228      * copied into the entity. Identical data will not be copied and if any 
229      * changes were made the DIRTY flag will be set. Will use the copyData
230      * method defined be each fields DataType object to copy the data which
231      * also controls if the copy is shallow or not.
232      */
233     public void copyData(IDataProvider propertySource) 
234     {
235         for(int j=0; j<this.getEntityDescriptor().getDataFieldCount(); j++)
236             this.setData(
237                     this.getEntityDescriptor().getFieldDescriptor(j), 
238                     this.getEntityDescriptor().getFieldDescriptor(j).getDataType().copyData(
239                             propertySource.getData(this.getEntityDescriptor().getFieldDescriptor(j).getCodeName())));
240     }
241     
242     /*** Returns true if the addressed entity field is contains a NULL value.
243      */
244     public boolean isDataNull(IFieldDescriptor fieldDescriptor)
245     {
246         return this.getData(fieldDescriptor)==null;
247     }
248         
249     /*** Returns the data value of the addressed data field. Can return NULL if
250      * the field excepts and contains a NULL value.
251      * @exception org.caleigo.core.exception.InvalidFieldException
252      */
253     public Object getData(IFieldDescriptor fieldDescriptor) 
254     {
255         // Check if fieldDescriptor is valid.
256         int fieldIndex = this.getEntityDescriptor().getFieldIndex(fieldDescriptor);
257         if(fieldIndex<0)
258             throw new org.caleigo.core.exception.InvalidFieldException("The field \""+fieldDescriptor.getCodeName()+"\" does not exist in object \""+this.getEntityDescriptor().getCodeName());
259 
260         // Get the data.
261         Object data = null;
262         if(fieldDescriptor instanceof ICalculatedFieldDescriptor)
263             data = ((ICalculatedFieldDescriptor)fieldDescriptor).getFieldDataFrom(this);
264         else
265             data =  this.getRawData(fieldIndex);
266         if(data instanceof IProxyData)
267             data = ((IProxyData)data).getData();
268         return data;
269     }
270     
271     /*** Sets the value of the addressed data field. Sets the DIRTY flag and
272      * clears the EMPTY flag but only if the new value differs from the old.
273      * If the value is actually changed then one or more EntityChangeExceptions
274      * will be fired.
275      * @exception org.caleigo.core.exception.InvalidFieldException
276      * @exception org.caleigo.core.exception.ReadOnlyViolationException
277      */
278     public void setData(IFieldDescriptor fieldDescriptor, Object data) 
279     {
280         // Check if fieldDescriptor is valid.
281         int fieldIndex = this.getEntityDescriptor().getFieldIndex(fieldDescriptor);
282         if(fieldIndex<0)
283             throw new org.caleigo.core.exception.InvalidFieldException("The field \""+fieldDescriptor.getCodeName()+"\" does not exist in object \""+this.getEntityDescriptor().getCodeName()+"\"!");
284         
285         // Throw exception if data is read only.
286 //        if(fieldDescriptor.isReadOnly())
287 //            throw new org.caleigo.core.exception.ReadOnlyViolationException("Data edit on read only field \""+fieldDescriptor.getCodeName()+"\" rejected!");
288         
289         // Check for change, break and ignore if no data has not been changed.
290         Object oldData = this.getData(fieldDescriptor);
291         if(!(data instanceof IProxyData) && fieldDescriptor.getDataType().compare(data, oldData)==0)
292             return;
293         
294         // Memorize original primary key if the addressed field is part of it.
295         if(fieldDescriptor.isIdentityField() && this.isPersistent() && mOriginIdentityQualifier==null)
296             mOriginIdentityQualifier = this.getOriginQualifier();
297         
298         ICalculatedFieldDescriptor[] calcFields2FireEventsFor = null;
299         Object[] oldDataArray = null;
300         // Set new data.
301         if(fieldDescriptor instanceof ICalculatedFieldDescriptor)
302             ((ICalculatedFieldDescriptor)fieldDescriptor).setFieldDataTo(this, data);
303         else
304         {
305             // Store calcFields and it's belonging data-value if the calcFields
306             // uses the changed field i.e. the parameter fieldDescriptor.
307             
308             // Check if this field is used by any calculated field at all
309             int calcFieldsCount = this.getEntityDescriptor().getFieldCount() - this.getEntityDescriptor().getDataFieldCount();
310             if( calcFieldsCount > 0 )
311             {
312                 // Create arrays.
313                 calcFields2FireEventsFor = new ICalculatedFieldDescriptor[calcFieldsCount];
314                 oldDataArray = new Object[calcFieldsCount];
315                 // Only store those that uses the changed fieldDescriptor.
316                 for(int j=this.getEntityDescriptor().getDataFieldCount(); j<this.getEntityDescriptor().getFieldCount(); j++)
317                 {
318                     ICalculatedFieldDescriptor calcField = (ICalculatedFieldDescriptor)this.getEntityDescriptor().getFieldDescriptor(j);
319                     IFieldDescriptor[] requiredFields = calcField.getRequiredFields();
320                     boolean usedByChangedField = false;
321                     for(int k=0; k<requiredFields.length; k++)
322                     {
323                         if (fieldDescriptor == requiredFields[k])
324                             usedByChangedField = true;
325                     }
326                     if (usedByChangedField)
327                     {
328                         int index = j - this.getEntityDescriptor().getDataFieldCount();
329                         calcFields2FireEventsFor[index] = calcField;
330                         oldDataArray[index] = calcField.getFieldDataFrom(this);
331                     }
332                 }
333             }
334             this.setRawData(fieldIndex, data);
335         }
336         // Update status flags.
337         this.clearStatusFlag(IEntity.EMPTY);
338         this.setDirtyFlag(fieldDescriptor);
339         
340         // Fire data change events. Use getData to get possible formation and
341         // type changes on the data.
342         this.fireDataChangedEvent(fieldDescriptor, oldData, this.getData(fieldDescriptor));
343         
344         // Fire data change events fore every calcFields that uses
345         // the changed fieldDescriptor.
346         if ( calcFields2FireEventsFor != null)
347         {
348             for(int j=0; j<calcFields2FireEventsFor.length; j++)
349             {
350                 if( calcFields2FireEventsFor[j] != null )
351                     this.fireDataChangedEvent(calcFields2FireEventsFor[j],
352                                                 oldDataArray[j],
353                                                 this.getData(calcFields2FireEventsFor[j]) );
354             }
355         }
356         
357     }
358 
359     /*** Clear resets all data in the entity to their defalt values and sets 
360      * the flags to reflect an empty unchanged data entity.
361      */
362     public void clear()
363     {
364         // Sets the default data values.
365         this.setDefaultValues();
366         
367         // Reset status flags.
368         this.clearStatusFlag(DIRTY | PERSISTENT);  
369         this.setStatusFlag(EMPTY);
370     }
371     
372     /*** Help method that validates the data contained in the called data
373      * object and returns a ValidationResult object. Call isValid on the 
374      * returned object to verify data validity. May never return null.
375      */
376     public ValidationResult validateData()
377     {
378         ValidationResult result = ValidationResult.VALID_RESULT;
379         for(int j=0; result.isValid() && j<this.getEntityDescriptor().getFieldCount(); j++)
380             result = this.getEntityDescriptor().getFieldDescriptor(j).validateData(this.getData(this.getEntityDescriptor().getFieldDescriptor(j)), this);
381         return result;
382     }
383 
384     /*** Returns true if the addressed entity field has been changed since 
385      * creation or the last syncronization with the persistent storage.
386      */
387     public boolean isFieldDirty(IFieldDescriptor fieldDescriptor) 
388     {
389         return mDirtyFlags!=null && mDirtyFlags.get(this.getEntityDescriptor().getFieldIndex(fieldDescriptor));
390     }
391     
392     /*** Returns true if any entity field in the entity has been changed since 
393      * creation or the last syncronization with the persistent storage.
394      */
395     public boolean isDirty() 
396     {
397         return (mStatusFlags & DIRTY)!=0;
398     }
399     
400     /*** Returns true for newly creted object that that has had no 
401      * changes from the default data set at the moment of creation.
402      */
403     public boolean isEmpty() 
404     {
405         return (mStatusFlags & EMPTY)!=0;
406     }
407     
408     /*** Returns true if the the entity reflects data that exists in a related 
409      * persistent storage. This means that newly created and deleted entities 
410      * will have this flag set to false. 
411      */
412     public boolean isPersistent() 
413     {
414         return (mStatusFlags & PERSISTENT)!=0;
415     }
416     
417     /*** Returns the data source that the entity object belongs to. 
418      * Newly created will normally return the default data source defined
419      * by the IDataSourceDescriptor that the entity is linked to trough it's
420      * IEntityDescriptor. Entities loaded from a persistent storage will return 
421      * the data source that identifies that storage/database.  
422      */
423     public IDataSource getDataSource()
424     {
425         return this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource();
426     }
427     
428     /*** Returns a identity qualifier that uniquely qualifies the entity in a 
429      * persistent storage. If the entity is PERSISTENT and any of the data in 
430      * the identity fields have changed since the storage syncronization the
431      * returned Qualifier will identify the stored persistent version of the
432      * entity and NOT& the updated local one.
433      */
434     public Qualifier getOriginQualifier() 
435     {
436         // Return origin qualifier if identity data has changed.
437         if(mOriginIdentityQualifier!=null)
438             return mOriginIdentityQualifier;
439         
440         // Else build and return the identity qualifier.
441         Qualifier identity = null;
442         for(int j=0; j<this.getEntityDescriptor().getDataFieldCount(); j++)
443             if(this.getEntityDescriptor().getFieldDescriptor(j).isIdentityField())
444             {
445                 identity = Qualifier.combine(identity, Qualifier.create(
446                         this.getEntityDescriptor().getFieldDescriptor(j), 
447                         this.getData(this.getEntityDescriptor().getFieldDescriptor(j))));
448             }
449         return identity;
450     }
451     
452     /*** Returns true if the data in all the entities IDENTIY fields are 
453      * considered equal according to their DataType class.
454      */
455     public boolean equals(Object entity) 
456     {
457         if(!(entity instanceof IEntity))
458             return false;
459         else
460             return this.compareTo(entity)==0;
461     }
462     
463     /*** Compares all data values between the objects if they are of the 
464      * same type. True is returned only if all contained data exactly matches 
465      * the compareded entity's data according to their DataType class.
466      */
467     public boolean equalsExactly(Object entity) 
468     {
469         // Return true if the same object.
470         if(this==entity)
471             return true;
472         
473         // Checks if the entities are of the same type.
474         boolean equal = entity instanceof IEntity && 
475                 this.getEntityDescriptor()==((IEntity)entity).getEntityDescriptor();
476         
477         // Compare all individal data values based on their DataType.
478         IFieldDescriptor field;
479         for(int j=0; equal && j<this.getEntityDescriptor().getFieldCount(); j++)
480         {
481             field = this.getEntityDescriptor().getFieldDescriptor(j);
482             equal = field.getDataType().compare(this.getData(field), ((IEntity)entity).getData(field))==0;
483         }
484         
485         return equal;
486     }
487     
488     // Clonable implementation -------------------------------------------------
489 /*    public Object clone() throws CloneNotSupportedException
490     {
491         // Copy falgs and support data.
492         AbstractEntity entity = (AbstractEntity) super.clone();
493         if(mDirtyFlags!=null)
494             entity.mDirtyFlags = (BitSet)mDirtyFlags.clone();
495 //        entity.mOriginIdentityQualifier = mOriginIdentityQualifier.clone();
496         
497         // Copy the contained field data. 
498         // Using copyData() here would be a waste of resources. 
499         for(int j=0; j<this.getEntityDescriptor().getDataFieldCount(); j++)
500             entity.setRawData(j, this.getRawData(j));   
501         
502         return entity;
503     }
504 */    
505     // IDataProvider implementation --------------------------------------------
506     public Object getData(String codeName) 
507     {
508         IFieldDescriptor descriptor = this.getEntityDescriptor().getFieldDescriptor(codeName);
509         if(descriptor!=null)
510             return this.getData(descriptor);        
511         else
512             return null;
513     }
514     
515     // IDataConsumer implementation -------------------------------------------
516     public void setData(String codeName, Object dataValue) 
517     {
518         IFieldDescriptor descriptor = this.getEntityDescriptor().getFieldDescriptor(codeName);
519         if(descriptor!=null)
520             this.setData(descriptor, dataValue);
521     }
522     
523     public void setData(IDataProvider dataProvider) 
524     {
525         for(int j=0; j<this.getEntityDescriptor().getDataFieldCount(); j++)
526             this.setData(
527                     this.getEntityDescriptor().getFieldDescriptor(j), 
528                     this.getEntityDescriptor().getFieldDescriptor(j).getDataType().copyData(
529                             dataProvider.getData(this.getEntityDescriptor().getFieldDescriptor(j).getCodeName())));
530     }
531     
532     // Comparable implementation -----------------------------------------------
533     
534     /*** Compares all identity data values between the objects if they are of the 
535      * same type that is are defined by the same entity descriptor. <BR><BR>
536      *
537      * If the compared object is not an IEntity object a ClassCastException
538      * will be thrown. If the object is an IEntity but of another type, that is 
539      * described by another entity descriptor, then the entities will first be 
540      * ordered according to type using the order MASTER_ENTITY, SLAVE_ENTITY, 
541      * LINK_ENTITY, STATIC_ENTITY, CUSTOM_ENTITY and secondly according to the
542      * entity descriptors code name.
543      */
544     public int compareTo(Object entity)
545     {
546         // Return true if the same object.
547         if(this==entity)
548             return 0;
549         
550         // Throw exception if the provided object is null or not an IEntity.
551         if(entity==null || !(entity instanceof IEntity))
552             throw new ClassCastException();
553         
554         // Checks if the entities are of the same type if not use special order.
555         if(this.getEntityDescriptor()!=((IEntity)entity).getEntityDescriptor())
556         {
557             if(this.getEntityDescriptor().getEntityType()!=((IEntity)entity).getEntityDescriptor().getEntityType())
558                 return this.getEntityDescriptor().getEntityType()-((IEntity)entity).getEntityDescriptor().getEntityType();
559             else
560                 return this.getEntityDescriptor().getCodeName().compareTo(((IEntity)entity).getEntityDescriptor().getCodeName());
561         }
562                 
563         // Compare all individal data values based on their DataType.
564         int compValue = 0;
565         IFieldDescriptor field;
566         for(int j=0; compValue==0 && j<this.getEntityDescriptor().getFieldCount(); j++)
567         {
568             field = this.getEntityDescriptor().getFieldDescriptor(j);
569             
570             if(field.isIdentityField())
571             {
572                 if(!field.isAutoGenerated() || (this.isPersistent() && ((IEntity)entity).isPersistent()))
573                     compValue = field.getDataType().compare(this.getData(field), ((IEntity)entity).getData(field));
574                 else if(this.isPersistent())
575                     compValue = ((IEntity)entity).hashCode()-this.hashCode();
576                 else
577                     compValue = this.hashCode()-((IEntity)entity).hashCode();
578             }
579         }
580         return compValue;
581     }
582     
583     // Access methods ----------------------------------------------------------
584     public void setStatusFlag(int flags)
585     {
586         int oldStatus = mStatusFlags;
587         mStatusFlags = (byte)(mStatusFlags | flags);
588         if((flags & ~oldStatus)!=0)
589             this.fireStatusChangedEvent(flags & ~oldStatus, true);
590     }
591     
592     public void clearStatusFlag(int flags)
593     {
594         int oldStatus = mStatusFlags;
595         mStatusFlags = (byte)(mStatusFlags & ~flags);
596         if((flags & oldStatus)!=0)
597             this.fireStatusChangedEvent(flags & oldStatus, false);
598         
599         // Syncronize dirty flag with field flags.
600         if((flags & DIRTY)!=0)
601         {
602             mDirtyFlags = null;
603             
604             // An entity that is not dirty can not have an origin qualifier.
605             // However if any identity field has changed and the DIRTY flag
606             // is manually reset on a PERSISTENT entity its state will be
607             // coruptet and subsequent store() operations will fail or may
608             // adress an unitended entity instance.
609             mOriginIdentityQualifier = null;
610         }
611     }
612     
613     /*** Adds IEntityListener to receive notifications of performed 
614      * data operations on the entity object.
615      */
616     public void addEntityListener(IEntityListener listener)
617     {
618         mEntityListener = (IEntityListener)CELEventMulticaster.add(mEntityListener, listener);
619     }
620     
621     /*** Removes the specified IEntityListener from the entity object.
622      */
623     public void removeEntityListener(IEntityListener listener)
624     {
625         mEntityListener = (IEntityListener)CELEventMulticaster.remove(mEntityListener, listener);
626     }
627     
628     /*** Adds IEntityChangeListener to receive notifications of changes in the  
629      * entity's status and data content. Note that changes can in specific 
630      * situations like during end-user editation be very frequent. 
631      */
632     public void addEntityChangeListener(IEntityChangeListener listener)
633     {
634         mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.add(mEntityChangeListener, listener);
635     }
636     
637     /*** Removes the specified IEntityListener from the entity object.
638      */
639     public void removeEntityChangeListener(IEntityChangeListener listener)
640     {
641         mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.remove(mEntityChangeListener, listener);
642     }
643 
644     // Help methods ------------------------------------------------------------
645     
646     /*** This method is expected to set the default values to the entity.
647      * By default the values will set using the default values specified by
648      * the entity´s descriptor. The method can be overriden to set dynamic
649      * and runtime specific values.
650      */
651     protected void setDefaultValues()
652     {
653         // Should handle null entity descriptor in a better way !!!
654         if(this.getEntityDescriptor() != null)
655             for(int j=0; j<this.getEntityDescriptor().getDataFieldCount(); j++)
656                 this.setRawData(j, this.getEntityDescriptor().getFieldDescriptor(j).getDefaultValue());
657     }
658     
659     /*** This help method is called when the called entity changes a data value. 
660      * It is called before the data change event is fired. The method is
661      * provided for optional override and does nothing by default.
662      *
663      * Note that setting other data as a response to the change will cause
664      * a new call to this method! This requires any overrides to take care
665      * not to cause a reqursive loop by being very selective when updating
666      * data values in this method.
667      */
668     protected void doOnDataChange(IFieldDescriptor fieldDescriptor, Object oldValue, Object newValue)
669     {
670     }
671         
672     /*** This help method is called when the called when an entity changes
673      * state from non-dirty to dirty. It is called before the status change 
674      * event is fired. The method is provided for optional override and 
675      * does nothing by default.
676      */
677     protected void doOnDirty()
678     {
679     }
680     
681     /*** This help method is called when the called entity changes state. 
682      * It is called before the status change event is fired. The method is
683      * provided for optional override and does nothing by default.
684      */
685     protected void doOnStateChange(int statusType, boolean newStatus)
686     {
687     }
688     
689     protected void setDirtyFlag(IFieldDescriptor fieldDescriptor)
690     {
691         if(mDirtyFlags==null)
692             mDirtyFlags = new BitSet(this.getEntityDescriptor().getDataFieldCount());
693         mDirtyFlags.set(this.getEntityDescriptor().getFieldIndex(fieldDescriptor));
694         this.setStatusFlag(DIRTY);
695     }
696     
697     protected void clearAllDirtyFlags()
698     {
699         mDirtyFlags = null;
700         this.clearStatusFlag(DIRTY);
701 
702         // An entity that is not dirty can not have an origin qualifier.
703         // However if any identity field has changed and the DIRTY flag
704         // is manually reset on a PERSISTENT entity its state will be
705         // coruptet and subsequent store() operations will fail or may
706         // adress an unitended entity instance.
707         mOriginIdentityQualifier = null;
708     }
709     
710     /*** Fires an EntityEvent with the provided operation type to all registered
711      * IEntityListener objects.
712      */
713     protected void fireOpPerformedEvent(int opType)
714     {
715         if(mEntityListener != null)
716             return;
717         else if(opType == EntityEvent.STORED)
718             mEntityListener.storePerformed(new EntityEvent(this, opType));
719         else if(opType == EntityEvent.DELETED)
720             mEntityListener.deletePerformed(new EntityEvent(this, opType));
721         else if(opType == EntityEvent.REFRESHED)
722             mEntityListener.refreshPerformed(new EntityEvent(this, opType));
723     }
724     
725     /*** Fires an EntityChangeEvent specifying a data field change to all 
726      * registered IEntityChangeListener objects.
727      */
728     protected void fireDataChangedEvent(IFieldDescriptor fieldDescriptor, Object oldValue, Object newValue)
729     {
730         this.doOnDataChange(fieldDescriptor, oldValue, newValue);
731 
732         if(mEntityChangeListener!=null)
733             mEntityChangeListener.dataChanged(new EntityChangeEvent(this, fieldDescriptor, oldValue, newValue));
734     }
735     
736     /*** Fires an EntityChangeEvent specifying a status change to all registered
737      * IEntityChangeListener objects.
738      */
739     protected void fireStatusChangedEvent(int statusType, boolean newStatus)
740     {
741         if((statusType & DIRTY)!=0 && newStatus)
742             this.doOnDirty();
743         this.doOnStateChange(statusType, newStatus);
744 
745         if(mEntityChangeListener!=null)
746             mEntityChangeListener.statusChanged(new EntityChangeEvent(this, statusType, newStatus));
747     }
748     
749     public long getDataLong(IFieldDescriptor field)
750     {
751         if(!this.isDataNull(field))
752             return ((Long)this.getData(field)).longValue();
753         else
754             return 0L;
755     }
756 
757     public void setDataLong(IFieldDescriptor field, long value)
758     {
759         this.setData(field, new Long(value));
760     }
761     
762     public int getDataInteger(IFieldDescriptor field)
763     {
764         if(!this.isDataNull(field))
765             return ((Integer)this.getData(field)).intValue();
766         else
767             return 0;
768     }
769 
770     public void setDataInteger(IFieldDescriptor field, int value)
771     {
772         this.setData(field, new Integer(value));
773     }
774     
775     public short getDataShort(IFieldDescriptor field)
776     {
777         if(!this.isDataNull(field))
778             return ((Short)this.getData(field)).shortValue();
779         else
780             return 0;
781     }
782 
783     public void setDataShort(IFieldDescriptor field, short value)
784     {
785         this.setData(field, new Short(value));
786     }
787     
788     public byte getDataByte(IFieldDescriptor field)
789     {
790         if(!this.isDataNull(field))
791             return ((Byte)this.getData(field)).byteValue();
792         else
793             return 0;
794     }
795 
796     public void setDataByte(IFieldDescriptor field, byte value)
797     {
798         this.setData(field, new Byte(value));
799     }
800     
801     public boolean getDataBoolean(IFieldDescriptor field)
802     {
803         if(!this.isDataNull(field))
804             return ((Boolean)this.getData(field)).booleanValue();
805         else
806             return false;
807     }
808 
809     public void setDataBoolean(IFieldDescriptor field, boolean value)
810     {
811         this.setData(field, new Boolean(value));
812     }
813     
814     public float getDataFloat(IFieldDescriptor field)
815     {
816         if(!this.isDataNull(field))
817             return ((Float)this.getData(field)).floatValue();
818         else
819             return 0;
820     }
821 
822     public void setDataFloat(IFieldDescriptor field, float value)
823     {
824         this.setData(field, new Float(value));
825     }
826     
827     public double getDataDouble(IFieldDescriptor field)
828     {
829         if(!this.isDataNull(field))
830             return ((Double)this.getData(field)).doubleValue();
831         else
832             return 0;
833     }
834 
835     public void setDataDouble(IFieldDescriptor field, double value)
836     {
837         this.setData(field, new Double(value));
838     }    
839 }