0001: /*
0002: * $Id: GenericEntity.java,v 1.20 2004/01/28 01:04:51 jonesde Exp $
0003: *
0004: * Copyright (c) 2002 The Open For Business Project - www.ofbiz.org
0005: *
0006: * Permission is hereby granted, free of charge, to any person obtaining a
0007: * copy of this software and associated documentation files (the "Software"),
0008: * to deal in the Software without restriction, including without limitation
0009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0010: * and/or sell copies of the Software, and to permit persons to whom the
0011: * Software is furnished to do so, subject to the following conditions:
0012: *
0013: * The above copyright notice and this permission notice shall be included
0014: * in all copies or substantial portions of the Software.
0015: *
0016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
0019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
0020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
0021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
0022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023: */
0024: package org.ofbiz.entity;
0025:
0026: import java.io.PrintWriter;
0027: import java.io.Serializable;
0028: import java.util.Collection;
0029: import java.util.HashMap;
0030: import java.util.Iterator;
0031: import java.util.LinkedList;
0032: import java.util.Locale;
0033: import java.util.Map;
0034: import java.util.MissingResourceException;
0035: import java.util.Observable;
0036: import java.util.ResourceBundle;
0037: import java.util.TreeSet;
0038:
0039: import org.ofbiz.base.util.Debug;
0040: import org.ofbiz.base.util.LocalizedMap;
0041: import org.ofbiz.base.util.UtilFormatOut;
0042: import org.ofbiz.base.util.UtilProperties;
0043: import org.ofbiz.base.util.UtilValidate;
0044: import org.ofbiz.base.util.UtilXml;
0045: import org.ofbiz.entity.jdbc.SqlJdbcUtil;
0046: import org.ofbiz.entity.model.ModelEntity;
0047: import org.ofbiz.entity.model.ModelField;
0048: import org.ofbiz.entity.model.ModelFieldType;
0049: import org.ofbiz.entity.util.ByteWrapper;
0050: import org.w3c.dom.Document;
0051: import org.w3c.dom.Element;
0052:
0053: /**
0054: * Generic Entity Value Object - Handles persisntence for any defined entity.
0055: * <p>Note that this class extends <code>Observable</code> to achieve change notification for
0056: * <code>Observer</code>s. Whenever a field changes the name of the field will be passed to
0057: * the <code>notifyObservers()</code> method, and through that to the <code>update()</code> method of each
0058: * <code>Observer</code>.
0059: *
0060: *@author <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>
0061: *@author <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a>
0062: *@version $Revision: 1.20 $
0063: *@since 2.0
0064: */
0065: public class GenericEntity extends Observable implements Map,
0066: LocalizedMap, Serializable, Comparable, Cloneable {
0067:
0068: public static final String module = GenericEntity.class.getName();
0069:
0070: /** Name of the GenericDelegator, used to reget the GenericDelegator when deserialized */
0071: protected String delegatorName = null;
0072:
0073: /** 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. */
0074: protected transient GenericDelegator internalDelegator = null;
0075:
0076: /** Contains the fields for this entity. Note that this should always be a
0077: * HashMap to allow for two things: non-synchronized reads (synchronized
0078: * writes are done through synchronized setters) and being able to store
0079: * null values. Null values are important because with them we can distinguish
0080: * between desiring to set a value to null and desiring to not modify the
0081: * current value on an update.
0082: */
0083: protected Map fields;
0084:
0085: /** Contains the entityName of this entity, necessary for efficiency when creating EJBs */
0086: protected String entityName = null;
0087:
0088: /** Contains the ModelEntity instance that represents the definition of this entity, not to be serialized */
0089: protected transient ModelEntity modelEntity = null;
0090:
0091: /** Denotes whether or not this entity has been modified, or is known to be out of sync with the persistent record */
0092: protected boolean modified = false;
0093:
0094: /** Used to specify whether or not this representation of the entity can be changed; generally cleared when this object comes from a cache */
0095: protected boolean mutable = true;
0096:
0097: /** 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 */
0098: protected boolean isFromEntitySync = false;
0099:
0100: /** Creates new GenericEntity */
0101: public GenericEntity() {
0102: this .entityName = null;
0103: this .modelEntity = null;
0104: this .fields = new HashMap();
0105: }
0106:
0107: /** Creates new GenericEntity */
0108: public GenericEntity(ModelEntity modelEntity) {
0109: if (modelEntity == null)
0110: throw new IllegalArgumentException(
0111: "Cannont create a GenericEntity with a null modelEntity parameter");
0112: this .modelEntity = modelEntity;
0113: this .entityName = modelEntity.getEntityName();
0114: this .fields = new HashMap();
0115: }
0116:
0117: /** Creates new GenericEntity from existing Map */
0118: public GenericEntity(ModelEntity modelEntity, Map fields) {
0119: if (modelEntity == null)
0120: throw new IllegalArgumentException(
0121: "Cannont create a GenericEntity with a null modelEntity parameter");
0122: this .modelEntity = modelEntity;
0123: this .entityName = modelEntity.getEntityName();
0124: this .fields = new HashMap();
0125: setFields(fields);
0126: }
0127:
0128: /** Copy Constructor: Creates new GenericEntity from existing GenericEntity */
0129: public GenericEntity(GenericEntity value) {
0130: this .entityName = value.modelEntity.getEntityName();
0131: this .modelEntity = value.modelEntity;
0132: this .fields = (value.fields == null ? new HashMap()
0133: : new HashMap(value.fields));
0134: this .delegatorName = value.delegatorName;
0135: this .internalDelegator = value.internalDelegator;
0136: }
0137:
0138: public void refreshFromValue(GenericEntity newValue)
0139: throws GenericEntityException {
0140: if (newValue == null) {
0141: throw new GenericEntityException(
0142: "Could not refresh value, new value not found for: "
0143: + this );
0144: }
0145: GenericPK this PK = this .getPrimaryKey();
0146: GenericPK newPK = newValue.getPrimaryKey();
0147: if (!this PK.equals(newPK)) {
0148: throw new GenericEntityException(
0149: "Could not refresh value, new value did not have the same primary key; this PK="
0150: + this PK + ", new value PK=" + newPK);
0151: }
0152: this .fields = newValue.fields;
0153: this .setDelegator(newValue.getDelegator());
0154: this .modified = false;
0155: }
0156:
0157: public boolean isModified() {
0158: return this .modified;
0159: }
0160:
0161: public void synchronizedWithDatasource() {
0162: this .modified = false;
0163: }
0164:
0165: public void removedFromDatasource() {
0166: // seems kind of minimal, but should do for now...
0167: this .modified = true;
0168: }
0169:
0170: public boolean isMutable() {
0171: return this .mutable;
0172: }
0173:
0174: public void setImmutable() {
0175: this .mutable = false;
0176: }
0177:
0178: /**
0179: * @return Returns the isFromEntitySync.
0180: */
0181: public boolean getIsFromEntitySync() {
0182: return this .isFromEntitySync;
0183: }
0184:
0185: /**
0186: * @param isFromEntitySync The isFromEntitySync to set.
0187: */
0188: public void setIsFromEntitySync(boolean isFromEntitySync) {
0189: this .isFromEntitySync = isFromEntitySync;
0190: }
0191:
0192: public String getEntityName() {
0193: return entityName;
0194: }
0195:
0196: public ModelEntity getModelEntity() {
0197: if (modelEntity == null) {
0198: if (entityName != null)
0199: modelEntity = this .getDelegator().getModelEntity(
0200: entityName);
0201: if (modelEntity == null) {
0202: throw new IllegalStateException(
0203: "[GenericEntity.getModelEntity] could not find modelEntity for entityName "
0204: + entityName);
0205: }
0206: }
0207: return modelEntity;
0208: }
0209:
0210: /** Get the GenericDelegator instance that created this value object and that is repsonsible for it.
0211: *@return GenericDelegator object
0212: */
0213: public GenericDelegator getDelegator() {
0214: if (internalDelegator == null) {
0215: if (delegatorName != null)
0216: internalDelegator = GenericDelegator
0217: .getGenericDelegator(delegatorName);
0218: if (internalDelegator == null) {
0219: throw new IllegalStateException(
0220: "[GenericEntity.getDelegator] could not find delegator with name "
0221: + delegatorName);
0222: }
0223: }
0224: return internalDelegator;
0225: }
0226:
0227: /** Set the GenericDelegator instance that created this value object and that is repsonsible for it. */
0228: public void setDelegator(GenericDelegator internalDelegator) {
0229: if (internalDelegator == null)
0230: return;
0231: this .delegatorName = internalDelegator.getDelegatorName();
0232: this .internalDelegator = internalDelegator;
0233: }
0234:
0235: public Object get(String name) {
0236: if (getModelEntity().getField(name) == null) {
0237: throw new IllegalArgumentException("[GenericEntity.get] \""
0238: + name + "\" is not a field of " + entityName);
0239: }
0240: return fields.get(name);
0241: }
0242:
0243: /** Returns true if the entity contains all of the primary key fields, but NO others. */
0244: public boolean isPrimaryKey() {
0245: TreeSet fieldKeys = new TreeSet(fields.keySet());
0246:
0247: for (int i = 0; i < getModelEntity().getPksSize(); i++) {
0248: if (!fieldKeys
0249: .contains(getModelEntity().getPk(i).getName()))
0250: return false;
0251: fieldKeys.remove(getModelEntity().getPk(i).getName());
0252: }
0253: if (!fieldKeys.isEmpty())
0254: return false;
0255: return true;
0256: }
0257:
0258: /** Returns true if the entity contains all of the primary key fields. */
0259: public boolean containsPrimaryKey() {
0260: TreeSet fieldKeys = new TreeSet(fields.keySet());
0261:
0262: for (int i = 0; i < getModelEntity().getPksSize(); i++) {
0263: if (!fieldKeys
0264: .contains(getModelEntity().getPk(i).getName()))
0265: return false;
0266: }
0267: return true;
0268: }
0269:
0270: /** Sets the named field to the passed value, even if the value is null
0271: * @param name The field name to set
0272: * @param value The value to set
0273: */
0274: public void set(String name, Object value) {
0275: set(name, value, true);
0276: }
0277:
0278: /** Sets the named field to the passed value. If value is null, it is only
0279: * set if the setIfNull parameter is true. This is useful because an update
0280: * will only set values that are included in the HashMap and will store null
0281: * values in the HashMap to the datastore. If a value is not in the HashMap,
0282: * it will be left unmodified in the datastore.
0283: * @param name The field name to set
0284: * @param value The value to set
0285: * @param setIfNull Specifies whether or not to set the value if it is null
0286: */
0287: public synchronized Object set(String name, Object value,
0288: boolean setIfNull) {
0289: if (!this .mutable) {
0290: // comment this out to disable the mutable check
0291: throw new IllegalStateException(
0292: "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.");
0293: }
0294:
0295: ModelField modelField = getModelEntity().getField(name);
0296: if (modelField == null) {
0297: throw new IllegalArgumentException("[GenericEntity.set] \""
0298: + name + "\" is not a field of " + entityName
0299: + ", must be one of: "
0300: + getModelEntity().fieldNameString());
0301: }
0302: if (value != null || setIfNull) {
0303: if (value instanceof Boolean) {
0304: // if this is a Boolean check to see if we should convert from an indicator or just leave as is
0305: ModelFieldType type = null;
0306:
0307: try {
0308: type = getDelegator().getEntityFieldType(
0309: getModelEntity(), modelField.getType());
0310: } catch (GenericEntityException e) {
0311: Debug.logWarning(e, module);
0312: }
0313: if (type == null)
0314: throw new IllegalArgumentException("Type "
0315: + modelField.getType() + " not found");
0316:
0317: try {
0318: int fieldType = SqlJdbcUtil.getType(type
0319: .getJavaType());
0320:
0321: if (fieldType != 9) {
0322: value = ((Boolean) value).booleanValue() ? "Y"
0323: : "N";
0324: }
0325: } catch (GenericNotImplementedException e) {
0326: throw new IllegalArgumentException(e.getMessage());
0327: }
0328: }
0329: Object old = fields.put(name, value);
0330:
0331: modified = true;
0332: this .setChanged();
0333: this .notifyObservers(name);
0334: return old;
0335: } else {
0336: return fields.get(name);
0337: }
0338: }
0339:
0340: public void dangerousSetNoCheckButFast(ModelField modelField,
0341: Object value) {
0342: if (modelField == null)
0343: throw new IllegalArgumentException(
0344: "Cannot set field with a null modelField");
0345: this .fields.put(modelField.getName(), value);
0346: }
0347:
0348: public Object dangerousGetNoCheckButFast(ModelField modelField) {
0349: if (modelField == null)
0350: throw new IllegalArgumentException(
0351: "Cannot get field with a null modelField");
0352: return this .fields.get(modelField.getName());
0353: }
0354:
0355: /** Sets the named field to the passed value, converting the value from a String to the corrent type using <code>Type.valueOf()</code>
0356: * @param name The field name to set
0357: * @param value The String value to convert and set
0358: */
0359: public void setString(String name, String value) {
0360: if (value == null) {
0361: set(name, null);
0362: return;
0363: }
0364:
0365: ModelField field = getModelEntity().getField(name);
0366: if (field == null)
0367: set(name, value); // this will get an error in the set() method...
0368:
0369: ModelFieldType type = null;
0370: try {
0371: type = getDelegator().getEntityFieldType(getModelEntity(),
0372: field.getType());
0373: } catch (GenericEntityException e) {
0374: Debug.logWarning(e, module);
0375: }
0376: if (type == null)
0377: throw new IllegalArgumentException("Type "
0378: + field.getType() + " not found");
0379: String fieldType = type.getJavaType();
0380:
0381: try {
0382: switch (SqlJdbcUtil.getType(fieldType)) {
0383: case 1:
0384: set(name, value);
0385: break;
0386:
0387: case 2:
0388: set(name, java.sql.Timestamp.valueOf(value));
0389: break;
0390:
0391: case 3:
0392: set(name, java.sql.Time.valueOf(value));
0393: break;
0394:
0395: case 4:
0396: set(name, java.sql.Date.valueOf(value));
0397: break;
0398:
0399: case 5:
0400: set(name, Integer.valueOf(value));
0401: break;
0402:
0403: case 6:
0404: set(name, Long.valueOf(value));
0405: break;
0406:
0407: case 7:
0408: set(name, Float.valueOf(value));
0409: break;
0410:
0411: case 8:
0412: set(name, Double.valueOf(value));
0413: break;
0414:
0415: case 9:
0416: set(name, Boolean.valueOf(value));
0417: break;
0418:
0419: case 10:
0420: set(name, value);
0421: break;
0422: }
0423: } catch (GenericNotImplementedException ex) {
0424: throw new IllegalArgumentException(ex.getMessage());
0425: }
0426: }
0427:
0428: /** Sets a field with an array of bytes, wrapping them automatically for easy use.
0429: * @param name The field name to set
0430: * @param bytes The byte array to be wrapped and set
0431: */
0432: public void setBytes(String name, byte[] bytes) {
0433: this .set(name, new ByteWrapper(bytes));
0434: }
0435:
0436: public Boolean getBoolean(String name) {
0437: Object obj = get(name);
0438:
0439: if (obj == null) {
0440: return null;
0441: }
0442: if (obj instanceof Boolean) {
0443: return (Boolean) obj;
0444: } else if (obj instanceof String) {
0445: String value = (String) obj;
0446:
0447: if ("Y".equals(value)) {
0448: return Boolean.TRUE;
0449: } else if ("N".equals(value)) {
0450: return Boolean.FALSE;
0451: } else {
0452: throw new IllegalArgumentException(
0453: "getBoolean could not map the String '" + value
0454: + "' to Boolean type");
0455: }
0456: } else {
0457: throw new IllegalArgumentException(
0458: "getBoolean could not map the object '"
0459: + obj.toString()
0460: + "' to Boolean type, unknown object type: "
0461: + obj.getClass().getName());
0462: }
0463: }
0464:
0465: public String getString(String name) {
0466: // might be nice to add some ClassCastException handling... and auto conversion? hmmm...
0467: Object object = get(name);
0468: if (object == null)
0469: return null;
0470: if (object instanceof java.lang.String) {
0471: return (String) object;
0472: } else {
0473: return object.toString();
0474: }
0475: }
0476:
0477: public java.sql.Timestamp getTimestamp(String name) {
0478: return (java.sql.Timestamp) get(name);
0479: }
0480:
0481: public java.sql.Time getTime(String name) {
0482: return (java.sql.Time) get(name);
0483: }
0484:
0485: public java.sql.Date getDate(String name) {
0486: return (java.sql.Date) get(name);
0487: }
0488:
0489: public Integer getInteger(String name) {
0490: return (Integer) get(name);
0491: }
0492:
0493: public Long getLong(String name) {
0494: return (Long) get(name);
0495: }
0496:
0497: public Float getFloat(String name) {
0498: return (Float) get(name);
0499: }
0500:
0501: public Double getDouble(String name) {
0502: return (Double) get(name);
0503: }
0504:
0505: public byte[] getBytes(String name) {
0506: ByteWrapper wrapper = (ByteWrapper) get(name);
0507: if (wrapper == null)
0508: return null;
0509: return wrapper.getBytes();
0510: }
0511:
0512: /** Checks a resource bundle for a value for this field using the entity name, the field name
0513: * and a composite of the Primary Key field values as a key. If no value is found in the
0514: * resource then the field value is returned. Uses the default-resource-name from the entity
0515: * definition as the resource name. To specify a resource name manually, use the other getResource method.
0516: *
0517: * So, the key in the resource bundle (properties file) should be as follows:
0518: * <entity-name>.<field-name>.<pk-field-value-1>.<pk-field-value-2>...<pk-field-value-n>
0519: * For example:
0520: * ProductType.description.FINISHED_GOOD
0521: *
0522: * @param name The name of the field on the entity
0523: * @param locale The locale to use when finding the ResourceBundle, if null uses the default
0524: * locale for the current instance of Java
0525: * @return If the corresponding resource is found and contains a key as described above, then that
0526: * property value is returned; otherwise returns the field value
0527: */
0528: public Object get(String name, Locale locale) {
0529: return get(name, null, locale);
0530: }
0531:
0532: /** Same as the getResource method that does not take resource name, but instead allows manually
0533: * specifying the resource name. In general you should use the other method for more consistent
0534: * naming and use of the corresponding properties files.
0535: * @param name The name of the field on the entity
0536: * @param resource The name of the resource to get the value from; if null defaults to the
0537: * default-resource-name on the entity definition, if specified there
0538: * @param locale The locale to use when finding the ResourceBundle, if null uses the default
0539: * locale for the current instance of Java
0540: * @return If the specified resource is found and contains a key as described above, then that
0541: * property value is returned; otherwise returns the field value
0542: */
0543: public Object get(String name, String resource, Locale locale) {
0544: Object fieldValue = get(name);
0545: if (UtilValidate.isEmpty(resource)) {
0546: resource = this .getModelEntity().getDefaultResourceName();
0547: // still empty? return the fieldValue
0548: if (UtilValidate.isEmpty(resource)) {
0549: //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);
0550: return fieldValue;
0551: }
0552: }
0553: ResourceBundle bundle = UtilProperties.getResourceBundle(
0554: resource, locale);
0555: if (bundle == null) {
0556: //Debug.logWarning("Tried to getResource value for field named " + name + " but no resource was found with the name " + resource + " in the locale " + locale, module);
0557: return fieldValue;
0558: }
0559:
0560: StringBuffer keyBuffer = new StringBuffer();
0561: // start with the Entity Name
0562: keyBuffer.append(this .getEntityName());
0563: // next add the Field Name
0564: keyBuffer.append('.');
0565: keyBuffer.append(name);
0566: // finish off by adding the values of all PK fields
0567: Iterator iter = this .getModelEntity().getPksIterator();
0568: while (iter != null && iter.hasNext()) {
0569: ModelField curField = (ModelField) iter.next();
0570: keyBuffer.append('.');
0571: keyBuffer.append(this .get(curField.getName()));
0572: }
0573:
0574: String bundleKey = keyBuffer.toString();
0575:
0576: Object resourceValue = null;
0577: try {
0578: resourceValue = bundle.getObject(bundleKey);
0579: } catch (MissingResourceException e) {
0580: return fieldValue;
0581: }
0582: if (resourceValue == null) {
0583: return fieldValue;
0584: } else {
0585: return resourceValue;
0586: }
0587: }
0588:
0589: public GenericPK getPrimaryKey() {
0590: Collection pkNames = new LinkedList();
0591: Iterator iter = this .getModelEntity().getPksIterator();
0592: while (iter != null && iter.hasNext()) {
0593: ModelField curField = (ModelField) iter.next();
0594: pkNames.add(curField.getName());
0595: }
0596: GenericPK newPK = new GenericPK(getModelEntity(), this
0597: .getFields(pkNames));
0598: newPK.setDelegator(this .getDelegator());
0599: return newPK;
0600: }
0601:
0602: /** go through the pks and for each one see if there is an entry in fields to set */
0603: public void setPKFields(Map fields) {
0604: setAllFields(fields, true, null, Boolean.TRUE);
0605: }
0606:
0607: /** go through the pks and for each one see if there is an entry in fields to set */
0608: public void setPKFields(Map fields, boolean setIfEmpty) {
0609: setAllFields(fields, setIfEmpty, null, Boolean.TRUE);
0610: }
0611:
0612: /** go through the non-pks and for each one see if there is an entry in fields to set */
0613: public void setNonPKFields(Map fields) {
0614: setAllFields(fields, true, null, Boolean.FALSE);
0615: }
0616:
0617: /** go through the non-pks and for each one see if there is an entry in fields to set */
0618: public void setNonPKFields(Map fields, boolean setIfEmpty) {
0619: setAllFields(fields, setIfEmpty, null, Boolean.FALSE);
0620: }
0621:
0622: /** Intelligently sets fields on this entity from the Map of fields passed in
0623: * @param fields The fields Map to get the values from
0624: * @param setIfEmpty Used to specify whether empty/null values in the field Map should over-write non-empty values in this entity
0625: * @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
0626: * @param pks If null, get all values, if TRUE just get PKs, if FALSE just get non-PKs
0627: */
0628: public void setAllFields(Map fields, boolean setIfEmpty,
0629: String namePrefix, Boolean pks) {
0630: if (fields == null) {
0631: return;
0632: }
0633: Iterator iter = null;
0634: if (pks != null) {
0635: if (pks.booleanValue()) {
0636: iter = this .getModelEntity().getPksIterator();
0637: } else {
0638: iter = this .getModelEntity().getNopksIterator();
0639: }
0640: } else {
0641: iter = this .getModelEntity().getFieldsIterator();
0642: }
0643:
0644: while (iter != null && iter.hasNext()) {
0645: ModelField curField = (ModelField) iter.next();
0646: String fieldName = curField.getName();
0647: String sourceFieldName = null;
0648: if (UtilValidate.isNotEmpty(namePrefix)) {
0649: sourceFieldName = namePrefix
0650: + Character.toUpperCase(fieldName.charAt(0))
0651: + fieldName.substring(1);
0652: } else {
0653: sourceFieldName = curField.getName();
0654: }
0655:
0656: if (fields.containsKey(sourceFieldName)) {
0657: Object field = fields.get(sourceFieldName);
0658:
0659: // if (Debug.verboseOn()) Debug.logVerbose("Setting field " + curField.getName() + ": " + field + ", setIfEmpty = " + setIfEmpty, module);
0660: if (setIfEmpty) {
0661: // if empty string, set to null
0662: if (field != null && field instanceof String
0663: && ((String) field).length() == 0) {
0664: this .set(curField.getName(), null);
0665: } else {
0666: this .set(curField.getName(), field);
0667: }
0668: } else {
0669: // okay, only set if not empty...
0670: if (field != null) {
0671: // if it's a String then we need to check length, otherwise set it because it's not null
0672: if (field instanceof String) {
0673: String fieldStr = (String) field;
0674:
0675: if (fieldStr.length() > 0) {
0676: this .set(curField.getName(), field);
0677: }
0678: } else {
0679: this .set(curField.getName(), field);
0680: }
0681: }
0682: }
0683: }
0684: }
0685: }
0686:
0687: /** Returns keys of entity fields
0688: * @return java.util.Collection
0689: */
0690: public Collection getAllKeys() {
0691: return fields.keySet();
0692: }
0693:
0694: /** Returns key/value pairs of entity fields
0695: * @return java.util.Map
0696: */
0697: public Map getAllFields() {
0698: return new HashMap(fields);
0699: }
0700:
0701: /** Used by clients to specify exactly the fields they are interested in
0702: * @param keysofFields the name of the fields the client is interested in
0703: * @return java.util.Map
0704: */
0705: public Map getFields(Collection keysofFields) {
0706: if (keysofFields == null)
0707: return null;
0708: Iterator keys = keysofFields.iterator();
0709: Object aKey = null;
0710: HashMap aMap = new HashMap();
0711:
0712: while (keys.hasNext()) {
0713: aKey = keys.next();
0714: aMap.put(aKey, this .fields.get(aKey));
0715: }
0716: return aMap;
0717: }
0718:
0719: /** Used by clients to update particular fields in the entity
0720: * @param keyValuePairs java.util.Map
0721: */
0722: public synchronized void setFields(Map keyValuePairs) {
0723: if (keyValuePairs == null)
0724: return;
0725: Iterator entries = keyValuePairs.entrySet().iterator();
0726: Map.Entry anEntry = null;
0727:
0728: // this could be implement with Map.putAll, but we'll leave it like this for the extra features it has
0729: while (entries.hasNext()) {
0730: anEntry = (Map.Entry) entries.next();
0731: this .set((String) anEntry.getKey(), anEntry.getValue(),
0732: true);
0733: }
0734: }
0735:
0736: public boolean matchesFields(Map keyValuePairs) {
0737: if (fields == null)
0738: return true;
0739: if (keyValuePairs == null || keyValuePairs.size() == 0)
0740: return true;
0741: Iterator entries = keyValuePairs.entrySet().iterator();
0742:
0743: while (entries.hasNext()) {
0744: Map.Entry anEntry = (Map.Entry) entries.next();
0745:
0746: if (!UtilValidate.areEqual(anEntry.getValue(), this .fields
0747: .get(anEntry.getKey()))) {
0748: return false;
0749: }
0750: }
0751: return true;
0752: }
0753:
0754: /** Used to indicate if locking is enabled for this entity
0755: * @return True if locking is enabled
0756: */
0757: public boolean lockEnabled() {
0758: return modelEntity.lock();
0759: }
0760:
0761: // ======= XML Related Methods ========
0762: public static Document makeXmlDocument(Collection values) {
0763: Document document = UtilXml
0764: .makeEmptyXmlDocument("entity-engine-xml");
0765:
0766: if (document == null)
0767: return null;
0768:
0769: addToXmlDocument(values, document);
0770: return document;
0771: }
0772:
0773: public static int addToXmlDocument(Collection values,
0774: Document document) {
0775: if (values == null)
0776: return 0;
0777: if (document == null)
0778: return 0;
0779:
0780: Element rootElement = document.getDocumentElement();
0781:
0782: Iterator iter = values.iterator();
0783: int numberAdded = 0;
0784:
0785: while (iter.hasNext()) {
0786: GenericValue value = (GenericValue) iter.next();
0787: Element valueElement = value.makeXmlElement(document);
0788:
0789: rootElement.appendChild(valueElement);
0790: numberAdded++;
0791: }
0792: return numberAdded;
0793: }
0794:
0795: /** Makes an XML Element object with an attribute for each field of the entity
0796: *@param document The XML Document that the new Element will be part of
0797: *@return org.w3c.dom.Element object representing this generic entity
0798: */
0799: public Element makeXmlElement(Document document) {
0800: return makeXmlElement(document, null);
0801: }
0802:
0803: /** Makes an XML Element object with an attribute for each field of the entity
0804: *@param document The XML Document that the new Element will be part of
0805: *@param prefix A prefix to put in front of the entity name in the tag name
0806: *@return org.w3c.dom.Element object representing this generic entity
0807: */
0808: public Element makeXmlElement(Document document, String prefix) {
0809: Element element = null;
0810:
0811: if (prefix == null)
0812: prefix = "";
0813: if (document != null)
0814: element = document.createElement(prefix
0815: + this .getEntityName());
0816: // else element = new ElementImpl(null, this.getEntityName());
0817: if (element == null)
0818: return null;
0819:
0820: Iterator modelFields = this .getModelEntity()
0821: .getFieldsIterator();
0822: while (modelFields.hasNext()) {
0823: ModelField modelField = (ModelField) modelFields.next();
0824: String name = modelField.getName();
0825: String value = this .getString(name);
0826:
0827: if (value != null) {
0828: if (value.indexOf('\n') >= 0
0829: || value.indexOf('\r') >= 0) {
0830: UtilXml.addChildElementCDATAValue(element, name,
0831: value, document);
0832: } else {
0833: element.setAttribute(name, value);
0834: }
0835: }
0836: }
0837:
0838: return element;
0839: }
0840:
0841: /** Writes XML text with an attribute or CDATA element for each field of the entity
0842: *@param writer A PrintWriter to write to
0843: *@param prefix A prefix to put in front of the entity name in the tag name
0844: */
0845: public void writeXmlText(PrintWriter writer, String prefix) {
0846: final int indent = 4;
0847:
0848: if (prefix == null)
0849: prefix = "";
0850:
0851: for (int i = 0; i < indent; i++)
0852: writer.print(' ');
0853: writer.print('<');
0854: writer.print(prefix);
0855: writer.print(this .getEntityName());
0856:
0857: // write attributes immediately and if a CDATA element is needed, put those in a Map for now
0858: Map cdataMap = new HashMap();
0859:
0860: Iterator modelFields = this .getModelEntity()
0861: .getFieldsIterator();
0862: while (modelFields.hasNext()) {
0863: ModelField modelField = (ModelField) modelFields.next();
0864: String name = modelField.getName();
0865: String valueStr = this .getString(name);
0866:
0867: if (valueStr != null) {
0868: StringBuffer value = new StringBuffer(valueStr);
0869: boolean needsCdata = false;
0870:
0871: // check each character, if line-feed or carriage-return is found set needsCdata to true; also look for invalid characters
0872: for (int i = 0; i < value.length(); i++) {
0873: char curChar = value.charAt(i);
0874: /* Some common character for these invalid values, have seen these are mostly from MS Word, but may be part of some standard:
0875: 5 = ...
0876: 18 = apostrophe
0877: 19 = left quotation mark
0878: 20 = right quotation mark
0879: 22 = –
0880: 23 = -
0881: 25 = tm
0882: *
0883: */
0884:
0885: switch (curChar) {
0886: case '\'':
0887: value.replace(i, i + 1, "'");
0888: break;
0889: case '"':
0890: value.replace(i, i + 1, """);
0891: break;
0892: case '&':
0893: value.replace(i, i + 1, "&");
0894: break;
0895: case '<':
0896: value.replace(i, i + 1, "<");
0897: break;
0898: case '>':
0899: value.replace(i, i + 1, ">");
0900: break;
0901: case 0xA: // newline, \n
0902: needsCdata = true;
0903: break;
0904: case 0xD: // carriage return, \r
0905: needsCdata = true;
0906: break;
0907: case 0x9: // tab
0908: // do nothing, just catch here so it doesn't get into the default
0909: break;
0910: case 0x5: // elipses (...)
0911: value.replace(i, i + 1, "...");
0912: break;
0913: case 0x12: // apostrophe
0914: value.replace(i, i + 1, "'");
0915: break;
0916: case 0x13: // left quote
0917: value.replace(i, i + 1, """);
0918: break;
0919: case 0x14: // right quote
0920: value.replace(i, i + 1, """);
0921: break;
0922: case 0x16: // big(?) dash -
0923: value.replace(i, i + 1, "-");
0924: break;
0925: case 0x17: // dash -
0926: value.replace(i, i + 1, "-");
0927: break;
0928: case 0x19: // tm
0929: value.replace(i, i + 1, "tm");
0930: break;
0931: default:
0932: if (curChar < 0x20) {
0933: // 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
0934: Debug
0935: .logInfo(
0936: "Removing invalid character ["
0937: + curChar
0938: + "] numeric value ["
0939: + (int) curChar
0940: + "] for field "
0941: + name
0942: + " of entity with PK: "
0943: + this
0944: .getPrimaryKey()
0945: .toString(),
0946: module);
0947: value.deleteCharAt(i);
0948: }
0949: }
0950: }
0951:
0952: if (needsCdata) {
0953: cdataMap.put(name, value.toString());
0954: } else {
0955: writer.print(' ');
0956: writer.print(name);
0957: writer.print("=\"");
0958: // encode the value...
0959: writer.print(value.toString());
0960: writer.print("\"");
0961: }
0962: }
0963: }
0964:
0965: if (cdataMap.size() == 0) {
0966: writer.println("/>");
0967: } else {
0968: writer.println('>');
0969:
0970: Iterator cdataIter = cdataMap.entrySet().iterator();
0971:
0972: while (cdataIter.hasNext()) {
0973: Map.Entry entry = (Map.Entry) cdataIter.next();
0974:
0975: for (int i = 0; i < (indent << 1); i++)
0976: writer.print(' ');
0977: writer.print('<');
0978: writer.print((String) entry.getKey());
0979: writer.print("><![CDATA[");
0980: writer.print((String) entry.getValue());
0981: writer.print("]]></");
0982: writer.print((String) entry.getKey());
0983: writer.println('>');
0984: }
0985:
0986: // don't forget to close the entity.
0987: for (int i = 0; i < indent; i++)
0988: writer.print(' ');
0989: writer.print("</");
0990: writer.print(this .getEntityName());
0991: writer.println(">");
0992: }
0993: }
0994:
0995: /** Determines the equality of two GenericEntity objects, overrides the default equals
0996: *@param obj The object (GenericEntity) to compare this two
0997: *@return boolean stating if the two objects are equal
0998: */
0999: public boolean equals(Object obj) {
1000: if (obj == null)
1001: return false;
1002:
1003: // from here, use the compareTo method since it is more efficient:
1004: try {
1005: return this .compareTo(obj) == 0;
1006: } catch (ClassCastException e) {
1007: return false;
1008: }
1009: }
1010:
1011: /** Creates a hashCode for the entity, using the default String hashCode and Map hashCode, overrides the default hashCode
1012: *@return Hashcode corresponding to this entity
1013: */
1014: public int hashCode() {
1015: // divide both by two (shift to right one bit) to maintain scale and add together
1016: return getEntityName().hashCode() >> 1 + fields.hashCode() >> 1;
1017: }
1018:
1019: /** Creates a String for the entity, overrides the default toString
1020: *@return String corresponding to this entity
1021: */
1022: public String toString() {
1023: StringBuffer theString = new StringBuffer();
1024:
1025: theString.append("[GenericEntity:");
1026: theString.append(getEntityName());
1027: theString.append(']');
1028:
1029: Iterator entries = fields.entrySet().iterator();
1030: Map.Entry anEntry = null;
1031:
1032: while (entries.hasNext()) {
1033: anEntry = (Map.Entry) entries.next();
1034: theString.append('[');
1035: theString.append(anEntry.getKey());
1036: theString.append(',');
1037: theString.append(anEntry.getValue());
1038: theString.append('(');
1039: theString.append(anEntry.getValue() != null ? anEntry
1040: .getValue().getClass().getName() : "");
1041: theString.append(')');
1042: theString.append(']');
1043: }
1044: return theString.toString();
1045: }
1046:
1047: /** Compares this GenericEntity to the passed object
1048: *@param obj Object to compare this to
1049: *@return int representing the result of the comparison (-1,0, or 1)
1050: */
1051: public int compareTo(Object obj) {
1052: // if null, it will push to the beginning
1053: if (obj == null)
1054: return -1;
1055:
1056: // rather than doing an if instanceof, just cast it and let it throw an exception if
1057: // it fails, this will be faster for the expected case (that it IS a GenericEntity)
1058: // if not a GenericEntity throw ClassCastException, as the spec says
1059: GenericEntity that = (GenericEntity) obj;
1060:
1061: int tempResult = this .entityName.compareTo(that.entityName);
1062:
1063: // if they did not match, we know the order, otherwise compare the primary keys
1064: if (tempResult != 0)
1065: return tempResult;
1066:
1067: // both have same entityName, should be the same so let's compare PKs
1068: int pksSize = modelEntity.getPksSize();
1069:
1070: for (int i = 0; i < pksSize; i++) {
1071: ModelField curField = modelEntity.getPk(i);
1072: Comparable this Val = (Comparable) this .fields.get(curField
1073: .getName());
1074: Comparable thatVal = (Comparable) that.fields.get(curField
1075: .getName());
1076:
1077: if (this Val == null) {
1078: if (thatVal == null)
1079: tempResult = 0;
1080: // if thisVal is null, but thatVal is not, return 1 to put this earlier in the list
1081: else
1082: tempResult = 1;
1083: } else {
1084: // if thatVal is null, put the other earlier in the list
1085: if (thatVal == null)
1086: tempResult = -1;
1087: else
1088: tempResult = this Val.compareTo(thatVal);
1089: }
1090: if (tempResult != 0)
1091: return tempResult;
1092: }
1093:
1094: // okay, if we got here it means the primaryKeys are exactly the SAME, so compare the rest of the fields
1095: int nopksSize = modelEntity.getNopksSize();
1096:
1097: for (int i = 0; i < nopksSize; i++) {
1098: ModelField curField = modelEntity.getNopk(i);
1099: Comparable this Val = (Comparable) this .fields.get(curField
1100: .getName());
1101: Comparable thatVal = (Comparable) that.fields.get(curField
1102: .getName());
1103:
1104: if (this Val == null) {
1105: if (thatVal == null)
1106: tempResult = 0;
1107: // if thisVal is null, but thatVal is not, return 1 to put this earlier in the list
1108: else
1109: tempResult = 1;
1110: } else {
1111: // if thatVal is null, put the other earlier in the list
1112: if (thatVal == null)
1113: tempResult = -1;
1114: else
1115: tempResult = this Val.compareTo(thatVal);
1116: }
1117: if (tempResult != 0)
1118: return tempResult;
1119: }
1120:
1121: // if we got here it means the two are exactly the same, so return tempResult, which should be 0
1122: return tempResult;
1123: }
1124:
1125: /** Clones this GenericEntity, this is a shallow clone & uses the default shallow HashMap clone
1126: *@return Object that is a clone of this GenericEntity
1127: */
1128: public Object clone() {
1129: GenericEntity newEntity = new GenericEntity(this );
1130:
1131: newEntity.setDelegator(internalDelegator);
1132: return newEntity;
1133: }
1134:
1135: // ---- Methods added to implement the Map interface: ----
1136:
1137: public Object remove(Object key) {
1138: return fields.remove(key);
1139: }
1140:
1141: public boolean containsKey(Object key) {
1142: return fields.containsKey(key);
1143: }
1144:
1145: public java.util.Set entrySet() {
1146: return fields.entrySet();
1147: }
1148:
1149: public Object put(Object key, Object value) {
1150: return this .set((String) key, value, true);
1151: }
1152:
1153: public void putAll(java.util.Map map) {
1154: this .setFields(map);
1155: }
1156:
1157: public void clear() {
1158: this .fields.clear();
1159: }
1160:
1161: public Object get(Object key) {
1162: try {
1163: return this .get((String) key);
1164: } catch (IllegalArgumentException e) {
1165: Debug
1166: .logWarning(
1167: e,
1168: "The field name (or key) ["
1169: + key
1170: + "] is not valid, printing IllegalArgumentException instead of throwing it because Map interface specification does not allow throwing that exception.",
1171: module);
1172: return null;
1173: }
1174: }
1175:
1176: public java.util.Set keySet() {
1177: return this .fields.keySet();
1178: }
1179:
1180: public boolean isEmpty() {
1181: return this .fields.isEmpty();
1182: }
1183:
1184: public java.util.Collection values() {
1185: return this .fields.values();
1186: }
1187:
1188: public boolean containsValue(Object value) {
1189: return this .fields.containsValue(value);
1190: }
1191:
1192: public int size() {
1193: return this.fields.size();
1194: }
1195: }
|