1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
44 public static final int SELECTABLE = 0x0F;
45 public static final int LISTABLE = 0x0E;
46 public static final int CACHEABLE = 0x0C;
47 public static final int SCANABLE = 0x08;
48
49 public static final int CREATABLE = 0x10;
50 public static final int EDITABLE = 0x20;
51 public static final int DELETABLE = 0x40;
52
53
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
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
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
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
112 mEntityActionList = new ArrayList(5);
113 this.addEntityAction(this.createStoreAction());
114 this.addEntityAction(this.createDeleteAction());
115 this.defineEntityActions();
116 }
117
118
119
120
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
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
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
159 entity = (IEntity)this.getEntityClass().newInstance();
160
161
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
177 if(qualifier!=null && !qualifier.canQualify(this))
178 throw new InvalidQualifierException("Qualifier can not qualifiy "+this+", qualifier was: "+qualifier);
179
180
181 if(this.getDataSourceDescriptor().getDefaultDataSource()==null)
182 throw new DataServiceNotFoundException("No default data source has been set for: "+this.getDataSourceDescriptor().getCodeName());
183
184
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
195 if(identityQualifier!=null && !identityQualifier.canUniquelyQualify(this))
196 throw new InvalidQualifierException("Qualifier can not uniquely qualifiy "+this);
197
198
199 if(this.getDataSourceDescriptor().getDefaultDataSource()==null)
200 throw new DataServiceNotFoundException("No default data source has been set for: "+this.getDataSourceDescriptor().getCodeName());
201
202
203 return this.getDataSourceDescriptor().getDefaultDataSource().loadEntity(this, identityQualifier);
204 }
205
206
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
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
514 for(int j=0; orderField==null && j<this.getFieldCount(); j++)
515 if(this.getFieldDescriptor(j).isNaturalOrder())
516 orderField = this.getFieldDescriptor(j);
517
518
519 for(int j=0; orderField==null && j<this.getFieldCount(); j++)
520 if(this.getFieldDescriptor(j).isIdentityField())
521 orderField = this.getFieldDescriptor(j);
522
523
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
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
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
668 protected static class Dezerializer implements java.io.Serializable
669 {
670
671 protected String mClassName;
672
673
674 public Dezerializer(String className)
675 {
676 mClassName = className;
677 }
678
679
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
701 public StoreEntityAction(IEntityDescriptor entityDescriptor)
702 {
703 super(entityDescriptor, STORE_ACTION, "Store", null);
704 }
705
706
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
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
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
752 public DeleteEntityAction(IEntityDescriptor entityDescriptor)
753 {
754 super(entityDescriptor, DELETE_ACTION, "Delete", null);
755 }
756
757
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
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 }