KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > entity > GenericEntity


1 /*
2  * $Id: GenericEntity.java 7032 2006-03-20 23:28:15Z jonesde $
3  *
4  * Copyright (c) 2002-2005 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */

24 package org.ofbiz.entity;
25
26 import java.io.PrintWriter JavaDoc;
27 import java.io.Serializable JavaDoc;
28 import java.math.BigDecimal JavaDoc;
29 import java.text.NumberFormat JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Locale JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.MissingResourceException JavaDoc;
36 import java.util.Observable JavaDoc;
37 import java.util.ResourceBundle JavaDoc;
38 import java.util.TreeSet JavaDoc;
39
40 import javolution.lang.Reusable;
41 import javolution.util.FastList;
42 import javolution.util.FastMap;
43
44 import org.ofbiz.base.util.Base64;
45 import org.ofbiz.base.util.Debug;
46 import org.ofbiz.base.util.ObjectType;
47 import org.ofbiz.base.util.UtilDateTime;
48 import org.ofbiz.base.util.UtilProperties;
49 import org.ofbiz.base.util.UtilValidate;
50 import org.ofbiz.base.util.UtilXml;
51 import org.ofbiz.base.util.collections.LocalizedMap;
52 import org.ofbiz.entity.condition.EntityCondition;
53 import org.ofbiz.entity.jdbc.SqlJdbcUtil;
54 import org.ofbiz.entity.model.ModelEntity;
55 import org.ofbiz.entity.model.ModelField;
56 import org.ofbiz.entity.model.ModelFieldType;
57 import org.ofbiz.entity.util.ByteWrapper;
58 import org.w3c.dom.Document JavaDoc;
59 import org.w3c.dom.Element JavaDoc;
60
61
62 /**
63  * Generic Entity Value Object - Handles persisntence for any defined entity.
64  * <p>Note that this class extends <code>Observable</code> to achieve change notification for
65  * <code>Observer</code>s. Whenever a field changes the name of the field will be passed to
66  * the <code>notifyObservers()</code> method, and through that to the <code>update()</code> method of each
67  * <code>Observer</code>.
68  *
69  *@author <a HREF="mailto:jonesde@ofbiz.org">David E. Jones</a>
70  *@author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
71  *@version $Rev: 7032 $
72  *@since 2.0
73  */

74 public class GenericEntity extends Observable JavaDoc implements Map JavaDoc, LocalizedMap, Serializable JavaDoc, Comparable JavaDoc, Cloneable JavaDoc, Reusable {
75     public static final String JavaDoc module = GenericEntity.class.getName();
76
77     public static final GenericEntity NULL_ENTITY = new NullGenericEntity();
78     public static final NullField NULL_FIELD = new NullField();
79
80     /** Name of the GenericDelegator, used to reget the GenericDelegator when deserialized */
81     protected String JavaDoc delegatorName = null;
82
83     /** Reference to an instance of GenericDelegator used to do some basic operations on this entity value. If null various methods in this class will fail. This is automatically set by the GenericDelegator for all GenericValue objects instantiated through it. You may set this manually for objects you instantiate manually, but it is optional. */
84     protected transient GenericDelegator internalDelegator = null;
85
86     /** Contains the fields for this entity. Note that this should always be a
87      * HashMap to allow for two things: non-synchronized reads (synchronized
88      * writes are done through synchronized setters) and being able to store
89      * null values. Null values are important because with them we can distinguish
90      * between desiring to set a value to null and desiring to not modify the
91      * current value on an update.
92      */

93     protected Map JavaDoc fields = FastMap.newInstance();
94
95     /** Contains the entityName of this entity, necessary for efficiency when creating EJBs */
96     protected String JavaDoc entityName = null;
97
98     /** Contains the ModelEntity instance that represents the definition of this entity, not to be serialized */
99     protected transient ModelEntity modelEntity = null;
100
101     /** Denotes whether or not this entity has been modified, or is known to be out of sync with the persistent record */
102     protected boolean modified = false;
103     protected boolean generateHashCode = true;
104     protected int cachedHashCode = 0;
105
106     /** Used to specify whether or not this representation of the entity can be changed; generally cleared when this object comes from a cache */
107     protected boolean mutable = true;
108
109     /** This is an internal field used to specify that a value has come from a sync process and that the auto-stamps should not be over-written */
110     protected boolean isFromEntitySync = false;
111     
112     /** Creates new GenericEntity - Should never be used, prefer the other options. */
113     protected GenericEntity() { }
114     
115     /** Creates new GenericEntity */
116     public static GenericEntity createGenericEntity(ModelEntity modelEntity) {
117         if (modelEntity == null) {
118             throw new IllegalArgumentException JavaDoc("Cannot create a GenericEntity with a null modelEntity parameter");
119         }
120         
121         GenericEntity newEntity = new GenericEntity();
122         newEntity.init(modelEntity);
123         return newEntity;
124     }
125
126     /** Creates new GenericEntity from existing Map */
127     public static GenericEntity createGenericEntity(ModelEntity modelEntity, Map JavaDoc fields) {
128         if (modelEntity == null) {
129             throw new IllegalArgumentException JavaDoc("Cannot create a GenericEntity with a null modelEntity parameter");
130         }
131         
132         GenericEntity newEntity = new GenericEntity();
133         newEntity.init(modelEntity, fields);
134         return newEntity;
135     }
136
137     /** Copy Factory Method: Creates new GenericEntity from existing GenericEntity */
138     public static GenericEntity createGenericEntity(GenericEntity value) {
139         if (value == null) {
140             throw new IllegalArgumentException JavaDoc("Cannot create a GenericEntity with a null value parameter");
141         }
142         
143         GenericEntity newEntity = new GenericEntity();
144         newEntity.init(value);
145         return newEntity;
146     }
147
148     /** Creates new GenericEntity */
149     protected void init(ModelEntity modelEntity) {
150         if (modelEntity == null) {
151             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity with a null modelEntity parameter");
152         }
153         this.modelEntity = modelEntity;
154         this.entityName = modelEntity.getEntityName();
155         
156         // check some things
157
if (this.entityName == null) {
158             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity with a null entityName in the modelEntity parameter");
159         }
160     }
161
162     /** Creates new GenericEntity from existing Map */
163     protected void init(ModelEntity modelEntity, Map JavaDoc fields) {
164         if (modelEntity == null) {
165             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity with a null modelEntity parameter");
166         }
167         this.modelEntity = modelEntity;
168         this.entityName = modelEntity.getEntityName();
169         setFields(fields);
170         
171         // check some things
172
if (this.entityName == null) {
173             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity with a null entityName in the modelEntity parameter");
174         }
175     }
176
177     /** Copy Constructor: Creates new GenericEntity from existing GenericEntity */
178     protected void init(GenericEntity value) {
179         if (value.modelEntity == null) {
180             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity from another GenericEntity with a null modelEntity in the value parameter");
181         }
182         this.entityName = value.modelEntity.getEntityName();
183         this.modelEntity = value.modelEntity;
184         if (value.fields != null) this.fields.putAll(value.fields);
185         this.delegatorName = value.delegatorName;
186         this.internalDelegator = value.internalDelegator;
187         
188         // check some things
189
if (this.entityName == null) {
190             throw new IllegalArgumentException JavaDoc("Cannont create a GenericEntity with a null entityName in the modelEntity parameter");
191         }
192     }
193
194     public void reset() {
195         // from GenericEntity
196
this.delegatorName = null;
197         this.internalDelegator = null;
198         this.fields = FastMap.newInstance();
199         this.entityName = null;
200         this.modelEntity = null;
201         this.modified = false;
202         this.generateHashCode = true;
203         this.cachedHashCode = 0;
204         this.mutable = true;
205         this.isFromEntitySync = false;
206     }
207     
208     public void refreshFromValue(GenericEntity newValue) throws GenericEntityException {
209         if (newValue == null) {
210             throw new GenericEntityException("Could not refresh value, new value not found for: " + this);
211         }
212         GenericPK thisPK = this.getPrimaryKey();
213         GenericPK newPK = newValue.getPrimaryKey();
214         if (!thisPK.equals(newPK)) {
215             throw new GenericEntityException("Could not refresh value, new value did not have the same primary key; this PK=" + thisPK + ", new value PK=" + newPK);
216         }
217         this.fields = newValue.fields;
218         this.setDelegator(newValue.getDelegator());
219         this.generateHashCode = newValue.generateHashCode;
220         this.cachedHashCode = newValue.cachedHashCode;
221         this.modified = false;
222     }
223
224     public boolean isModified() {
225         return this.modified;
226     }
227
228     public void synchronizedWithDatasource() {
229         this.modified = false;
230     }
231
232     public void removedFromDatasource() {
233         // seems kind of minimal, but should do for now...
234
this.modified = true;
235     }
236
237     public boolean isMutable() {
238         return this.mutable;
239     }
240
241     public void setImmutable() {
242         this.mutable = false;
243     }
244
245     /**
246      * @return Returns the isFromEntitySync.
247      */

248     public boolean getIsFromEntitySync() {
249         return this.isFromEntitySync;
250     }
251
252     /**
253      * @param isFromEntitySync The isFromEntitySync to set.
254      */

255     public void setIsFromEntitySync(boolean isFromEntitySync) {
256         this.isFromEntitySync = isFromEntitySync;
257     }
258     
259     public String JavaDoc getEntityName() {
260         return entityName;
261     }
262
263     public ModelEntity getModelEntity() {
264         if (modelEntity == null) {
265             if (entityName != null) modelEntity = this.getDelegator().getModelEntity(entityName);
266             if (modelEntity == null) {
267                 throw new IllegalStateException JavaDoc("[GenericEntity.getModelEntity] could not find modelEntity for entityName " + entityName);
268             }
269         }
270         return modelEntity;
271     }
272
273     /** Get the GenericDelegator instance that created this value object and that is repsonsible for it.
274      *@return GenericDelegator object
275      */

276     public GenericDelegator getDelegator() {
277         if (internalDelegator == null) {
278             if (delegatorName == null) delegatorName = "default";
279             if (delegatorName != null) internalDelegator = GenericDelegator.getGenericDelegator(delegatorName);
280             if (internalDelegator == null) {
281                 throw new IllegalStateException JavaDoc("[GenericEntity.getDelegator] could not find delegator with name " + delegatorName);
282             }
283         }
284         return internalDelegator;
285     }
286
287     /** Set the GenericDelegator instance that created this value object and that is repsonsible for it. */
288     public void setDelegator(GenericDelegator internalDelegator) {
289         if (internalDelegator == null) return;
290         this.delegatorName = internalDelegator.getDelegatorName();
291         this.internalDelegator = internalDelegator;
292     }
293
294     public Object JavaDoc get(String JavaDoc name) {
295         if (getModelEntity().getField(name) == null) {
296             throw new IllegalArgumentException JavaDoc("[GenericEntity.get] \"" + name + "\" is not a field of " + entityName);
297         }
298         return fields.get(name);
299     }
300
301     /** Returns true if the entity contains all of the primary key fields, but NO others. */
302     public boolean isPrimaryKey() {
303         return isPrimaryKey(false);
304     }
305     public boolean isPrimaryKey(boolean requireValue) {
306         TreeSet JavaDoc fieldKeys = new TreeSet JavaDoc(this.fields.keySet());
307         Iterator JavaDoc pkIter = getModelEntity().getPksIterator();
308         while (pkIter.hasNext()) {
309             ModelField curPk = (ModelField) pkIter.next();
310             String JavaDoc fieldName = curPk.getName();
311             if (requireValue) {
312                 if (this.fields.get(fieldName) == null) return false;
313             } else {
314                 if (!this.fields.containsKey(fieldName)) return false;
315             }
316             fieldKeys.remove(fieldName);
317         }
318         if (!fieldKeys.isEmpty()) return false;
319         return true;
320     }
321
322     /** Returns true if the entity contains all of the primary key fields. */
323     public boolean containsPrimaryKey() {
324         return containsPrimaryKey(false);
325     }
326     public boolean containsPrimaryKey(boolean requireValue) {
327         //TreeSet fieldKeys = new TreeSet(fields.keySet());
328
Iterator JavaDoc pkIter = getModelEntity().getPksIterator();
329         while (pkIter.hasNext()) {
330             ModelField curPk = (ModelField) pkIter.next();
331             String JavaDoc fieldName = curPk.getName();
332             if (requireValue) {
333                 if (this.fields.get(fieldName) == null) return false;
334             } else {
335                 if (!this.fields.containsKey(fieldName)) return false;
336             }
337         }
338         return true;
339     }
340
341     /** Sets the named field to the passed value, even if the value is null
342      * @param name The field name to set
343      * @param value The value to set
344      */

345     public void set(String JavaDoc name, Object JavaDoc value) {
346         set(name, value, true);
347     }
348
349     /** Sets the named field to the passed value. If value is null, it is only
350      * set if the setIfNull parameter is true. This is useful because an update
351      * will only set values that are included in the HashMap and will store null
352      * values in the HashMap to the datastore. If a value is not in the HashMap,
353      * it will be left unmodified in the datastore.
354      * @param name The field name to set
355      * @param value The value to set
356      * @param setIfNull Specifies whether or not to set the value if it is null
357      */

358     public synchronized Object JavaDoc set(String JavaDoc name, Object JavaDoc value, boolean setIfNull) {
359         if (!this.mutable) {
360             // comment this out to disable the mutable check
361
throw new IllegalStateException JavaDoc("This object has been flagged as immutable (unchangeable), probably because it came from an Entity Engine cache. Cannot set a value in an immutable entity object.");
362         }
363
364         ModelField modelField = getModelEntity().getField(name);
365         if (modelField == null) {
366             throw new IllegalArgumentException JavaDoc("[GenericEntity.set] \"" + name + "\" is not a field of " + entityName + ", must be one of: " + getModelEntity().fieldNameString());
367         }
368         if (value != null || setIfNull) {
369             ModelFieldType type = null;
370             try {
371                 type = getDelegator().getEntityFieldType(getModelEntity(), modelField.getType());
372             } catch (GenericEntityException e) {
373                 Debug.logWarning(e, module);
374             }
375             if (type == null) {
376                 throw new IllegalArgumentException JavaDoc("Type " + modelField.getType() + " not found");
377             }
378
379             if (value instanceof Boolean JavaDoc) {
380                 // if this is a Boolean check to see if we should convert from an indicator or just leave as is
381
try {
382                     int fieldType = SqlJdbcUtil.getType(type.getJavaType());
383                     if (fieldType != 9) {
384                         value = ((Boolean JavaDoc) value).booleanValue() ? "Y" : "N";
385                     }
386                 } catch (GenericNotImplementedException e) {
387                     throw new IllegalArgumentException JavaDoc(e.getMessage());
388                 }
389             } else if (value != null) {
390                 // make sure the type matches the field Java type
391
if (!ObjectType.instanceOf(value, type.getJavaType())) {
392                     String JavaDoc errMsg = "In entity field [" + this.getEntityName() + "." + name + "] set the value passed in [" + value.getClass().getName() + "] is not compatible with the Java type of the field [" + type.getJavaType() + "]";
393                     // eventually we should do this, but for now we'll do a "soft" failure: throw new IllegalArgumentException(errMsg);
394
Debug.logWarning(errMsg, module);
395                 }
396             }
397             Object JavaDoc old = fields.put(name, value);
398
399             generateHashCode = true;
400             modified = true;
401             this.setChanged();
402             this.notifyObservers(name);
403             return old;
404         } else {
405             return fields.get(name);
406         }
407     }
408
409     public void dangerousSetNoCheckButFast(ModelField modelField, Object JavaDoc value) {
410         if (modelField == null) throw new IllegalArgumentException JavaDoc("Cannot set field with a null modelField");
411         generateHashCode = true;
412         this.fields.put(modelField.getName(), value);
413     }
414
415     public Object JavaDoc dangerousGetNoCheckButFast(ModelField modelField) {
416         if (modelField == null) throw new IllegalArgumentException JavaDoc("Cannot get field with a null modelField");
417         return this.fields.get(modelField.getName());
418     }
419
420     /** Sets the named field to the passed value, converting the value from a String to the corrent type using <code>Type.valueOf()</code>
421      * @param name The field name to set
422      * @param value The String value to convert and set
423      */

424     public void setString(String JavaDoc name, String JavaDoc value) {
425         if (value == null) {
426             set(name, null);
427             return;
428         }
429         
430         boolean isNullString = false;
431         if ("null".equals(value)) {
432             // count this as a null too, but only for numbers and stuff, not for Strings
433
isNullString = true;
434         }
435         
436         ModelField field = getModelEntity().getField(name);
437         if (field == null) set(name, value); // this will get an error in the set() method...
438

439         ModelFieldType type = null;
440         try {
441             type = getDelegator().getEntityFieldType(getModelEntity(), field.getType());
442         } catch (GenericEntityException e) {
443             Debug.logWarning(e, module);
444         }
445         if (type == null) throw new IllegalArgumentException JavaDoc("Type " + field.getType() + " not found");
446         String JavaDoc fieldType = type.getJavaType();
447
448         try {
449             switch (SqlJdbcUtil.getType(fieldType)) {
450             case 1:
451                 set(name, value);
452                 break;
453
454             case 2:
455                 set(name, isNullString ? null : java.sql.Timestamp.valueOf(value));
456                 break;
457
458             case 3:
459                 set(name, isNullString ? null : java.sql.Time.valueOf(value));
460                 break;
461
462             case 4:
463                 set(name, isNullString ? null : java.sql.Date.valueOf(value));
464                 break;
465
466             case 5:
467                 set(name, isNullString ? null : Integer.valueOf(value));
468                 break;
469
470             case 6:
471                 set(name, isNullString ? null : Long.valueOf(value));
472                 break;
473
474             case 7:
475                 set(name, isNullString ? null : Float.valueOf(value));
476                 break;
477
478             case 8:
479                 set(name, isNullString ? null : Double.valueOf(value));
480                 break;
481
482             case 9:
483                 set(name, isNullString ? null : Boolean.valueOf(value));
484                 break;
485
486             case 10: // BigDecimal
487
set(name, isNullString ? null : new BigDecimal JavaDoc(value));
488                 break;
489
490             case 11: // Object
491
set(name, value);
492                 break;
493
494             case 12: // java.sql.Blob
495
// TODO: any better way to handle Blob from String?
496
set(name, value);
497                 break;
498
499             case 13: // java.sql.Clob
500
// TODO: any better way to handle Clob from String?
501
set(name, value);
502                 break;
503
504             case 14: // java.util.Date
505
set(name, UtilDateTime.toDate(fieldType));
506                 break;
507
508             case 15: // java.util.Collection
509
// TODO: how to convert from String to Collection? ie what should the default behavior be?
510
set(name, value);
511                 break;
512             }
513         } catch (GenericNotImplementedException ex) {
514             throw new IllegalArgumentException JavaDoc(ex.getMessage());
515         }
516     }
517
518     /** Sets a field with an array of bytes, wrapping them automatically for easy use.
519      * @param name The field name to set
520      * @param bytes The byte array to be wrapped and set
521      */

522     public void setBytes(String JavaDoc name, byte[] bytes) {
523         this.set(name, new ByteWrapper(bytes));
524     }
525
526     public Boolean JavaDoc getBoolean(String JavaDoc name) {
527         Object JavaDoc obj = get(name);
528
529         if (obj == null) {
530             return null;
531         }
532         if (obj instanceof Boolean JavaDoc) {
533             return (Boolean JavaDoc) obj;
534         } else if (obj instanceof String JavaDoc) {
535             String JavaDoc value = (String JavaDoc) obj;
536
537             if ("Y".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value)) {
538                 return Boolean.TRUE;
539             } else if ("N".equalsIgnoreCase(value) || "F".equalsIgnoreCase(value)) {
540                 return Boolean.FALSE;
541             } else {
542                 throw new IllegalArgumentException JavaDoc("getBoolean could not map the String '" + value + "' to Boolean type");
543             }
544         } else {
545             throw new IllegalArgumentException JavaDoc("getBoolean could not map the object '" + obj.toString() + "' to Boolean type, unknown object type: " + obj.getClass().getName());
546         }
547     }
548
549     public String JavaDoc getString(String JavaDoc name) {
550         // might be nice to add some ClassCastException handling... and auto conversion? hmmm...
551
Object JavaDoc object = get(name);
552         if (object == null) return null;
553         if (object instanceof java.lang.String JavaDoc) {
554             return (String JavaDoc) object;
555         } else {
556             return object.toString();
557         }
558     }
559
560     public java.sql.Timestamp JavaDoc getTimestamp(String JavaDoc name) {
561         return (java.sql.Timestamp JavaDoc) get(name);
562     }
563
564     public java.sql.Time JavaDoc getTime(String JavaDoc name) {
565         return (java.sql.Time JavaDoc) get(name);
566     }
567
568     public java.sql.Date JavaDoc getDate(String JavaDoc name) {
569         return (java.sql.Date JavaDoc) get(name);
570     }
571
572     public Integer JavaDoc getInteger(String JavaDoc name) {
573         return (Integer JavaDoc) get(name);
574     }
575
576     public Long JavaDoc getLong(String JavaDoc name) {
577         return (Long JavaDoc) get(name);
578     }
579
580     public Float JavaDoc getFloat(String JavaDoc name) {
581         return (Float JavaDoc) get(name);
582     }
583
584     public Double JavaDoc getDouble(String JavaDoc name) {
585         // this "hack" is needed for now until the Double/BigDecimal issues are all resolved
586
Object JavaDoc value = get(name);
587         if (value instanceof BigDecimal JavaDoc) {
588             return new Double JavaDoc(((BigDecimal JavaDoc) value).doubleValue());
589         } else {
590             return (Double JavaDoc) get(name);
591         }
592     }
593
594     public BigDecimal JavaDoc getBigDecimal(String JavaDoc name) {
595         // this "hack" is needed for now until the Double/BigDecimal issues are all resolved
596
// NOTE: for this to be used properly it should really be used as the java-type in the field type def XML files
597
Object JavaDoc value = get(name);
598         if (value instanceof Double JavaDoc) {
599             return new BigDecimal JavaDoc(((Double JavaDoc) value).doubleValue());
600         } else {
601             return (BigDecimal JavaDoc) get(name);
602         }
603     }
604
605     public byte[] getBytes(String JavaDoc name) {
606         Object JavaDoc value = get(name);
607         if (value == null) {
608             return null;
609         }
610         if (value instanceof ByteWrapper) {
611             ByteWrapper wrapper = (ByteWrapper) value;
612             return wrapper.getBytes();
613         }
614         if (value instanceof byte[]) {
615             return (byte[]) value;
616         }
617         // uh-oh, this shouldn't happen...
618
throw new IllegalArgumentException JavaDoc("In call to getBytes the value is not a supported type, should be byte[] or ByteWrapper, is: " + value.getClass().getName());
619     }
620
621     /** Checks a resource bundle for a value for this field using the entity name, the field name
622      * and a composite of the Primary Key field values as a key. If no value is found in the
623      * resource then the field value is returned. Uses the default-resource-name from the entity
624      * definition as the resource name. To specify a resource name manually, use the other getResource method.
625      *
626      * So, the key in the resource bundle (properties file) should be as follows:
627      * <entity-name>.<field-name>.<pk-field-value-1>.<pk-field-value-2>...<pk-field-value-n>
628      * For example:
629      * ProductType.description.FINISHED_GOOD
630      *
631      * @param name The name of the field on the entity
632      * @param locale The locale to use when finding the ResourceBundle, if null uses the default
633      * locale for the current instance of Java
634      * @return If the corresponding resource is found and contains a key as described above, then that
635      * property value is returned; otherwise returns the field value
636      */

637     public Object JavaDoc get(String JavaDoc name, Locale JavaDoc locale) {
638         return get(name, null, locale);
639     }
640
641     /** Same as the getResource method that does not take resource name, but instead allows manually
642      * specifying the resource name. In general you should use the other method for more consistent
643      * naming and use of the corresponding properties files.
644      * @param name The name of the field on the entity
645      * @param resource The name of the resource to get the value from; if null defaults to the
646      * default-resource-name on the entity definition, if specified there
647      * @param locale The locale to use when finding the ResourceBundle, if null uses the default
648      * locale for the current instance of Java
649      * @return If the specified resource is found and contains a key as described above, then that
650      * property value is returned; otherwise returns the field value
651      */

652     public Object JavaDoc get(String JavaDoc name, String JavaDoc resource, Locale JavaDoc locale) {
653         Object JavaDoc fieldValue = get(name);
654         if (UtilValidate.isEmpty(resource)) {
655             resource = this.getModelEntity().getDefaultResourceName();
656             // still empty? return the fieldValue
657
if (UtilValidate.isEmpty(resource)) {
658                 //Debug.logWarning("Tried to getResource value for field named " + name + " but no resource name was passed to the method or specified in the default-resource-name attribute of the entity definition", module);
659
return fieldValue;
660             }
661         }
662         ResourceBundle JavaDoc bundle = null;
663         try {
664             bundle = UtilProperties.getResourceBundle(resource, locale);
665         } catch (IllegalArgumentException JavaDoc e) {
666             bundle = null;
667         }
668         if (bundle == null) {
669             //Debug.logWarning("Tried to getResource value for field named " + name + " but no resource was found with the name " + resource + " in the locale " + locale, module);
670
return fieldValue;
671         }
672
673         StringBuffer JavaDoc keyBuffer = new StringBuffer JavaDoc();
674         // start with the Entity Name
675
keyBuffer.append(this.getEntityName());
676         // next add the Field Name
677
keyBuffer.append('.');
678         keyBuffer.append(name);
679         // finish off by adding the values of all PK fields
680
Iterator JavaDoc iter = this.getModelEntity().getPksIterator();
681         while (iter != null && iter.hasNext()) {
682             ModelField curField = (ModelField) iter.next();
683             keyBuffer.append('.');
684             keyBuffer.append(this.get(curField.getName()));
685         }
686
687         String JavaDoc bundleKey = keyBuffer.toString();
688
689         Object JavaDoc resourceValue = null;
690         try {
691             resourceValue = bundle.getObject(bundleKey);
692         } catch (MissingResourceException JavaDoc e) {
693             return fieldValue;
694         }
695         if (resourceValue == null) {
696             return fieldValue;
697         } else {
698             return resourceValue;
699         }
700     }
701
702     public GenericPK getPrimaryKey() {
703         Collection JavaDoc pkNames = FastList.newInstance();
704         Iterator JavaDoc iter = this.getModelEntity().getPksIterator();
705         while (iter != null && iter.hasNext()) {
706             ModelField curField = (ModelField) iter.next();
707             pkNames.add(curField.getName());
708         }
709         GenericPK newPK = GenericPK.create(getModelEntity(), this.getFields(pkNames));
710         newPK.setDelegator(this.getDelegator());
711         return newPK;
712     }
713
714     /** go through the pks and for each one see if there is an entry in fields to set */
715     public void setPKFields(Map JavaDoc fields) {
716         setAllFields(fields, true, null, Boolean.TRUE);
717     }
718
719     /** go through the pks and for each one see if there is an entry in fields to set */
720     public void setPKFields(Map JavaDoc fields, boolean setIfEmpty) {
721         setAllFields(fields, setIfEmpty, null, Boolean.TRUE);
722     }
723
724     /** go through the non-pks and for each one see if there is an entry in fields to set */
725     public void setNonPKFields(Map JavaDoc fields) {
726         setAllFields(fields, true, null, Boolean.FALSE);
727     }
728
729     /** go through the non-pks and for each one see if there is an entry in fields to set */
730     public void setNonPKFields(Map JavaDoc fields, boolean setIfEmpty) {
731         setAllFields(fields, setIfEmpty, null, Boolean.FALSE);
732     }
733     
734     
735     /** Intelligently sets fields on this entity from the Map of fields passed in
736      * @param fields The fields Map to get the values from
737      * @param setIfEmpty Used to specify whether empty/null values in the field Map should over-write non-empty values in this entity
738      * @param namePrefix If not null or empty will be pre-pended to each field name (upper-casing the first letter of the field name first), and that will be used as the fields Map lookup name instead of the field-name
739      * @param pks If null, get all values, if TRUE just get PKs, if FALSE just get non-PKs
740      */

741     public void setAllFields(Map JavaDoc fields, boolean setIfEmpty, String JavaDoc namePrefix, Boolean JavaDoc pks) {
742         if (fields == null) {
743             return;
744         }
745         Iterator JavaDoc iter = null;
746         if (pks != null) {
747             if (pks.booleanValue()) {
748                 iter = this.getModelEntity().getPksIterator();
749             } else {
750                 iter = this.getModelEntity().getNopksIterator();
751             }
752         } else {
753             iter = this.getModelEntity().getFieldsIterator();
754         }
755             
756         while (iter != null && iter.hasNext()) {
757             ModelField curField = (ModelField) iter.next();
758             String JavaDoc fieldName = curField.getName();
759             String JavaDoc sourceFieldName = null;
760             if (UtilValidate.isNotEmpty(namePrefix)) {
761                 sourceFieldName = namePrefix + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
762             } else {
763                 sourceFieldName = curField.getName();
764             }
765             
766             if (fields.containsKey(sourceFieldName)) {
767                 Object JavaDoc field = fields.get(sourceFieldName);
768
769                 // if (Debug.verboseOn()) Debug.logVerbose("Setting field " + curField.getName() + ": " + field + ", setIfEmpty = " + setIfEmpty, module);
770
if (setIfEmpty) {
771                     // if empty string, set to null
772
if (field != null && field instanceof String JavaDoc && ((String JavaDoc) field).length() == 0) {
773                         this.set(curField.getName(), null);
774                     } else {
775                         this.set(curField.getName(), field);
776                     }
777                 } else {
778                     // okay, only set if not empty...
779
if (field != null) {
780                         // if it's a String then we need to check length, otherwise set it because it's not null
781
if (field instanceof String JavaDoc) {
782                             String JavaDoc fieldStr = (String JavaDoc) field;
783
784                             if (fieldStr.length() > 0) {
785                                 this.set(curField.getName(), field);
786                             }
787                         } else {
788                             this.set(curField.getName(), field);
789                         }
790                     }
791                 }
792             }
793         }
794     }
795
796     /** Returns keys of entity fields
797      * @return java.util.Collection
798      */

799     public Collection JavaDoc getAllKeys() {
800         return fields.keySet();
801     }
802
803     /** Returns key/value pairs of entity fields
804      * @return java.util.Map
805      */

806     public Map JavaDoc getAllFields() {
807         Map JavaDoc newMap = FastMap.newInstance();
808         newMap.putAll(this.fields);
809         return newMap;
810     }
811
812     /** Used by clients to specify exactly the fields they are interested in
813      * @param keysofFields the name of the fields the client is interested in
814      * @return java.util.Map
815      */

816     public Map JavaDoc getFields(Collection JavaDoc keysofFields) {
817         if (keysofFields == null) return null;
818         Iterator JavaDoc keys = keysofFields.iterator();
819         Object JavaDoc aKey = null;
820         Map JavaDoc aMap = FastMap.newInstance();
821
822         while (keys.hasNext()) {
823             aKey = keys.next();
824             aMap.put(aKey, this.fields.get(aKey));
825         }
826         return aMap;
827     }
828
829     /** Used by clients to update particular fields in the entity
830      * @param keyValuePairs java.util.Map
831      */

832     public synchronized void setFields(Map JavaDoc keyValuePairs) {
833         if (keyValuePairs == null) return;
834         Iterator JavaDoc entries = keyValuePairs.entrySet().iterator();
835         Map.Entry JavaDoc anEntry = null;
836
837         // this could be implement with Map.putAll, but we'll leave it like this for the extra features it has
838
while (entries.hasNext()) {
839             anEntry = (Map.Entry JavaDoc) entries.next();
840             this.set((String JavaDoc) anEntry.getKey(), anEntry.getValue(), true);
841         }
842     }
843
844     public boolean matchesFields(Map JavaDoc keyValuePairs) {
845         if (fields == null) return true;
846         if (keyValuePairs == null || keyValuePairs.size() == 0) return true;
847         Iterator JavaDoc entries = keyValuePairs.entrySet().iterator();
848
849         while (entries.hasNext()) {
850             Map.Entry JavaDoc anEntry = (Map.Entry JavaDoc) entries.next();
851
852             if (!UtilValidate.areEqual(anEntry.getValue(), this.fields.get(anEntry.getKey()))) {
853                 return false;
854             }
855         }
856         return true;
857     }
858
859     /** Used to indicate if locking is enabled for this entity
860      * @return True if locking is enabled
861      */

862     public boolean lockEnabled() {
863         return getModelEntity().lock();
864     }
865
866     // ======= XML Related Methods ========
867
public static Document JavaDoc makeXmlDocument(Collection JavaDoc values) {
868         Document JavaDoc document = UtilXml.makeEmptyXmlDocument("entity-engine-xml");
869
870         if (document == null) return null;
871
872         addToXmlDocument(values, document);
873         return document;
874     }
875
876     public static int addToXmlDocument(Collection JavaDoc values, Document JavaDoc document) {
877         return addToXmlElement(values, document, document.getDocumentElement());
878     }
879
880     public static int addToXmlElement(Collection JavaDoc values, Document JavaDoc document, Element JavaDoc element) {
881         if (values == null) return 0;
882         if (document == null) return 0;
883
884         Iterator JavaDoc iter = values.iterator();
885         int numberAdded = 0;
886
887         while (iter.hasNext()) {
888             GenericValue value = (GenericValue) iter.next();
889             Element JavaDoc valueElement = value.makeXmlElement(document);
890
891             element.appendChild(valueElement);
892             numberAdded++;
893         }
894         return numberAdded;
895     }
896
897     /** Makes an XML Element object with an attribute for each field of the entity
898      *@param document The XML Document that the new Element will be part of
899      *@return org.w3c.dom.Element object representing this generic entity
900      */

901     public Element JavaDoc makeXmlElement(Document JavaDoc document) {
902         return makeXmlElement(document, null);
903     }
904
905     /** Makes an XML Element object with an attribute for each field of the entity
906      *@param document The XML Document that the new Element will be part of
907      *@param prefix A prefix to put in front of the entity name in the tag name
908      *@return org.w3c.dom.Element object representing this generic entity
909      */

910     public Element JavaDoc makeXmlElement(Document JavaDoc document, String JavaDoc prefix) {
911         Element JavaDoc element = null;
912
913         if (prefix == null) prefix = "";
914         if (document != null) element = document.createElement(prefix + this.getEntityName());
915         // else element = new ElementImpl(null, this.getEntityName());
916
if (element == null) return null;
917
918         Iterator JavaDoc modelFields = this.getModelEntity().getFieldsIterator();
919         while (modelFields.hasNext()) {
920             ModelField modelField = (ModelField) modelFields.next();
921             String JavaDoc name = modelField.getName();
922             String JavaDoc value = this.getString(name);
923
924             if (value != null) {
925                 if (value.indexOf('\n') >= 0 || value.indexOf('\r') >= 0) {
926                     UtilXml.addChildElementCDATAValue(element, name, value, document);
927                 } else {
928                     element.setAttribute(name, value);
929                 }
930             }
931         }
932
933         return element;
934     }
935
936     /** Writes XML text with an attribute or CDATA element for each field of the entity
937      *@param writer A PrintWriter to write to
938      *@param prefix A prefix to put in front of the entity name in the tag name
939      */

940     public void writeXmlText(PrintWriter JavaDoc writer, String JavaDoc prefix) {
941         final int indent = 4;
942
943         if (prefix == null) prefix = "";
944
945         for (int i = 0; i < indent; i++) writer.print(' ');
946         writer.print('<');
947         writer.print(prefix);
948         writer.print(this.getEntityName());
949
950         // write attributes immediately and if a CDATA element is needed, put those in a Map for now
951
Map JavaDoc cdataMap = FastMap.newInstance();
952
953         Iterator JavaDoc modelFields = this.getModelEntity().getFieldsIterator();
954         while (modelFields.hasNext()) {
955             ModelField modelField = (ModelField) modelFields.next();
956             String JavaDoc name = modelField.getName();
957             
958             String JavaDoc type = modelField.getType();
959             if (type != null && type.equals("blob")) {
960                 Object JavaDoc obj = get(name);
961                 boolean b1 = obj instanceof byte [];
962                 if (b1) {
963                     byte [] binData = (byte [])obj;
964                     String JavaDoc strData = new String JavaDoc(Base64.base64Encode(binData));
965                     cdataMap.put(name, strData);
966                 } else {
967                     Debug.logWarning("Field:" + name + " is not of type 'byte[]'. obj: " + obj, module);
968                 }
969             } else {
970                 String JavaDoc valueStr = this.getString(name);
971     
972                 if (valueStr != null) {
973                     StringBuffer JavaDoc value = new StringBuffer JavaDoc(valueStr);
974                     boolean needsCdata = false;
975                     
976                     // check each character, if line-feed or carriage-return is found set needsCdata to true; also look for invalid characters
977
for (int i = 0; i < value.length(); i++) {
978                         char curChar = value.charAt(i);
979                         /* Some common character for these invalid values, have seen these are mostly from MS Word, but may be part of some standard:
980                          5 = ...
981                          18 = apostrophe
982                          19 = left quotation mark
983                          20 = right quotation mark
984                          22 = –
985                          23 = -
986                          25 = tm
987                          *
988                          */

989                         
990                         switch (curChar) {
991                         case '\'':
992                             value.replace(i, i+1, "&apos;");
993                             break;
994                         case '"':
995                             value.replace(i, i+1, "&quot;");
996                             break;
997                         case '&':
998                             value.replace(i, i+1, "&amp;");
999                             break;
1000                        case '<':
1001                            value.replace(i, i+1, "&lt;");
1002                            break;
1003                        case '>':
1004                            value.replace(i, i+1, "&gt;");
1005                            break;
1006                        case 0xA: // newline, \n
1007
needsCdata = true;
1008                            break;
1009                        case 0xD: // carriage return, \r
1010
needsCdata = true;
1011                            break;
1012                        case 0x9: // tab
1013
// do nothing, just catch here so it doesn't get into the default
1014
break;
1015                        case 0x5: // elipses (...)
1016
value.replace(i, i+1, "...");
1017                            break;
1018                        case 0x12: // apostrophe
1019
value.replace(i, i+1, "&apos;");
1020                            break;
1021                        case 0x13: // left quote
1022
value.replace(i, i+1, "&quot;");
1023                            break;
1024                        case 0x14: // right quote
1025
value.replace(i, i+1, "&quot;");
1026                            break;
1027                        case 0x16: // big(?) dash -
1028
value.replace(i, i+1, "-");
1029                            break;
1030                        case 0x17: // dash -
1031
value.replace(i, i+1, "-");
1032                            break;
1033                        case 0x19: // tm
1034
value.replace(i, i+1, "tm");
1035                            break;
1036                        default:
1037                            if (curChar < 0x20) {
1038                                // if it is less that 0x20 at this point it is invalid because the only valid values < 0x20 are 0x9, 0xA, 0xD as caught above
1039
Debug.logInfo("Removing invalid character [" + curChar + "] numeric value [" + (int) curChar + "] for field " + name + " of entity with PK: " + this.getPrimaryKey().toString(), module);
1040                                value.deleteCharAt(i);
1041                            }
1042                        }
1043                    }
1044                    
1045                    if (needsCdata) {
1046                        // use valueStr instead of the escaped value, not needed or wanted in a CDATA block
1047
cdataMap.put(name, valueStr);
1048                    } else {
1049                        writer.print(' ');
1050                        writer.print(name);
1051                        writer.print("=\"");
1052                        // encode the value...
1053
writer.print(value.toString());
1054                        writer.print("\"");
1055                    }
1056                }
1057            }
1058        }
1059
1060        if (cdataMap.size() == 0) {
1061            writer.println("/>");
1062        } else {
1063            writer.println('>');
1064
1065            Iterator JavaDoc cdataIter = cdataMap.entrySet().iterator();
1066
1067            while (cdataIter.hasNext()) {
1068                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) cdataIter.next();
1069
1070                for (int i = 0; i < (indent << 1); i++) writer.print(' ');
1071                writer.print('<');
1072                writer.print((String JavaDoc) entry.getKey());
1073                writer.print("><![CDATA[");
1074                writer.print((String JavaDoc) entry.getValue());
1075                writer.print("]]></");
1076                writer.print((String JavaDoc) entry.getKey());
1077                writer.println('>');
1078            }
1079
1080            // don't forget to close the entity.
1081
for (int i = 0; i < indent; i++) writer.print(' ');
1082            writer.print("</");
1083            writer.print(this.getEntityName());
1084            writer.println(">");
1085        }
1086    }
1087
1088    /** Determines the equality of two GenericEntity objects, overrides the default equals
1089     *@param obj The object (GenericEntity) to compare this two
1090     *@return boolean stating if the two objects are equal
1091     */

1092    public boolean equals(Object JavaDoc obj) {
1093        if (obj == null) return false;
1094
1095        // from here, use the compareTo method since it is more efficient:
1096
try {
1097            return this.compareTo(obj) == 0;
1098        } catch (ClassCastException JavaDoc e) {
1099            return false;
1100        }
1101    }
1102
1103    /** Creates a hashCode for the entity, using the default String hashCode and Map hashCode, overrides the default hashCode
1104     *@return Hashcode corresponding to this entity
1105     */

1106    public int hashCode() {
1107        // divide both by two (shift to right one bit) to maintain scale and add together
1108
if (generateHashCode) {
1109            cachedHashCode = 0;
1110            if (getEntityName() != null) {
1111                cachedHashCode += getEntityName().hashCode() >> 1;
1112            }
1113            cachedHashCode += fields.hashCode() >> 1;
1114            generateHashCode = false;
1115        }
1116        return cachedHashCode;
1117    }
1118
1119    /** Creates a String for the entity, overrides the default toString
1120     *@return String corresponding to this entity
1121     */

1122    public String JavaDoc toString() {
1123        StringBuffer JavaDoc theString = new StringBuffer JavaDoc();
1124
1125        theString.append("[GenericEntity:");
1126        theString.append(getEntityName());
1127        theString.append(']');
1128
1129        Iterator JavaDoc keyNames = new TreeSet JavaDoc(fields.keySet()).iterator();
1130        while (keyNames.hasNext()) {
1131            String JavaDoc curKey = (String JavaDoc) keyNames.next();
1132            Object JavaDoc curValue = fields.get(curKey);
1133            theString.append('[');
1134            theString.append(curKey);
1135            theString.append(',');
1136            theString.append(curValue);
1137            theString.append('(');
1138            theString.append(curValue != null ? curValue.getClass().getName() : "");
1139            theString.append(')');
1140            theString.append(']');
1141        }
1142        return theString.toString();
1143    }
1144
1145    /** Compares this GenericEntity to the passed object
1146     *@param obj Object to compare this to
1147     *@return int representing the result of the comparison (-1,0, or 1)
1148     */

1149    public int compareTo(Object JavaDoc obj) {
1150        // if null, it will push to the beginning
1151
if (obj == null) return -1;
1152
1153        // rather than doing an if instanceof, just cast it and let it throw an exception if
1154
// it fails, this will be faster for the expected case (that it IS a GenericEntity)
1155
// if not a GenericEntity throw ClassCastException, as the spec says
1156
GenericEntity that = (GenericEntity) obj;
1157
1158        int tempResult = this.entityName.compareTo(that.entityName);
1159
1160        // if they did not match, we know the order, otherwise compare the primary keys
1161
if (tempResult != 0) return tempResult;
1162
1163        // both have same entityName, should be the same so let's compare PKs
1164
Iterator JavaDoc pkIter = getModelEntity().getPksIterator();
1165        while (pkIter.hasNext()) {
1166            ModelField curField = (ModelField) pkIter.next();
1167            Comparable JavaDoc thisVal = (Comparable JavaDoc) this.fields.get(curField.getName());
1168            Comparable JavaDoc thatVal = (Comparable JavaDoc) that.fields.get(curField.getName());
1169
1170            if (thisVal == null) {
1171                if (thatVal == null)
1172                    tempResult = 0;
1173                // if thisVal is null, but thatVal is not, return 1 to put this earlier in the list
1174
else
1175                    tempResult = 1;
1176            } else {
1177                // if thatVal is null, put the other earlier in the list
1178
if (thatVal == null)
1179                    tempResult = -1;
1180                else
1181                    tempResult = thisVal.compareTo(thatVal);
1182            }
1183            if (tempResult != 0) return tempResult;
1184        }
1185
1186        // okay, if we got here it means the primaryKeys are exactly the SAME, so compare the rest of the fields
1187
Iterator JavaDoc nopkIter = getModelEntity().getNopksIterator();
1188        while (nopkIter.hasNext()) {
1189            ModelField curField = (ModelField) nopkIter.next();
1190            if (!curField.getIsAutoCreatedInternal()) {
1191                Comparable JavaDoc thisVal = (Comparable JavaDoc) this.fields.get(curField.getName());
1192                Comparable JavaDoc thatVal = (Comparable JavaDoc) that.fields.get(curField.getName());
1193
1194                if (thisVal == null) {
1195                    if (thatVal == null) {
1196                        tempResult = 0;
1197                    // if thisVal is null, but thatVal is not, return 1 to put this earlier in the list
1198
} else {
1199                        tempResult = 1;
1200                    }
1201                } else {
1202                    // if thatVal is null, put the other earlier in the list
1203
if (thatVal == null) {
1204                        tempResult = -1;
1205                    } else {
1206                        tempResult = thisVal.compareTo(thatVal);
1207                    }
1208                }
1209                if (tempResult != 0) return tempResult;
1210            }
1211        }
1212
1213        // if we got here it means the two are exactly the same, so return tempResult, which should be 0
1214
return tempResult;
1215    }
1216
1217    /** Clones this GenericEntity, this is a shallow clone & uses the default shallow HashMap clone
1218     *@return Object that is a clone of this GenericEntity
1219     */

1220    public Object JavaDoc clone() {
1221        GenericEntity newEntity = new GenericEntity();
1222        newEntity.init(this);
1223
1224        newEntity.setDelegator(internalDelegator);
1225        return newEntity;
1226    }
1227
1228    // ---- Methods added to implement the Map interface: ----
1229

1230    public Object JavaDoc remove(Object JavaDoc key) {
1231        return this.fields.remove(key);
1232    }
1233
1234    public boolean containsKey(Object JavaDoc key) {
1235        return this.fields.containsKey(key);
1236    }
1237
1238    public java.util.Set JavaDoc entrySet() {
1239        return Collections.unmodifiableSet(this.fields.entrySet());
1240    }
1241
1242    public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
1243        return this.set((String JavaDoc) key, value, true);
1244    }
1245
1246    public void putAll(java.util.Map JavaDoc map) {
1247        this.setFields(map);
1248    }
1249
1250    public void clear() {
1251        this.fields.clear();
1252    }
1253
1254    public Object JavaDoc get(Object JavaDoc key) {
1255        try {
1256            return this.get((String JavaDoc) key);
1257        } catch (IllegalArgumentException JavaDoc e) {
1258            Debug.logWarning(e, "The field name (or key) [" + key + "] is not valid, printing IllegalArgumentException instead of throwing it because Map interface specification does not allow throwing that exception.", module);
1259            return null;
1260        }
1261    }
1262
1263    public java.util.Set JavaDoc keySet() {
1264        return Collections.unmodifiableSet(this.fields.keySet());
1265    }
1266
1267    public boolean isEmpty() {
1268        return this.fields.isEmpty();
1269    }
1270
1271    public java.util.Collection JavaDoc values() {
1272        return Collections.unmodifiableCollection(this.fields.values());
1273    }
1274
1275    public boolean containsValue(Object JavaDoc value) {
1276        return this.fields.containsValue(value);
1277    }
1278
1279    public int size() {
1280        return this.fields.size();
1281    }
1282
1283    public boolean matches(EntityCondition condition) {
1284        return condition.entityMatches(this);
1285    }
1286
1287    public static interface NULL {
1288    }
1289
1290    public static class NullGenericEntity extends GenericEntity implements NULL {
1291        protected NullGenericEntity() { }
1292        
1293        public String JavaDoc toString() {
1294            return "[null-entity]";
1295        }
1296    }
1297    
1298    public static class NullField implements NULL {
1299        protected NullField() { }
1300    
1301        public String JavaDoc toString() {
1302            return "[null-field]";
1303        }
1304    }
1305}
1306
Popular Tags