/* * Copyright (C) NetStruxr, Inc. All rights reserved. * * This software is published under the terms of the NetStruxr * Public Software License version 0.5, a copy of which has been * included with this distribution in the LICENSE.NPL file. */ package er.extensions.eof; import java.util.Enumeration; import org.apache.log4j.Logger; import com.webobjects.eoaccess.EOEntity; import com.webobjects.eoaccess.EOUtilities; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.eocontrol.EOCustomObject; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eocontrol.EOFaultHandler; import com.webobjects.eocontrol.EOFetchSpecification; import com.webobjects.eocontrol.EOGlobalID; import com.webobjects.eocontrol.EOObjectStoreCoordinator; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EORelationshipManipulation; import com.webobjects.eocontrol.EOSharedEditingContext; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSValidation; import er.extensions.crypting.ERXCrypto; import er.extensions.foundation.ERXArrayUtilities; import er.extensions.foundation.ERXProperties; import er.extensions.foundation.ERXUtilities; import er.extensions.validation.ERXValidationException; import er.extensions.validation.ERXValidationFactory; /** * This class contains a bunch of extensions to the * regular {@link com.webobjects.eocontrol.EOCustomObject} class. Of notable * interest it contains built in support for generating * primary keys via the {@link ERXGeneratesPrimaryKeyInterface}, * support for an augmented transaction methods like <code> * willUpdate</code> and <code>didDelete</code> and a bunch * of handy utility methods like <code>committedSnapshotValueForKey * </code>. At the moment it is required that those wishing to take * advantage of templatized and localized validation exceptions * need to subclass this class. Hopefully in the future we can * get rid of this requirement. * Also, this class supports auto-updating of inverse relationships. You can * simply call <code>eo.setFoo(other), eo.takeValueForKey(other), * eo.addObjectToBothSidesOfRelationshipWithKey(other, "foo")</code> or <code>eo.addToFoos(other)</code> * and the inverse relationship will get * updated for you automagically, so that you don't need to call * <code>other.addToBars(eo)</code> or <code>other.setBar(eo)</code>. Doing so doesn't hurt, though. * Giving a <code>null</code> value of removing the object from a to-many will result in the inverse * relationship getting cleared. * <p> * This feature should greatly help readability and reduce the number errors you make when you * forget to update an inverse relationship. To turn this feature on, you must set the system default * <code>er.extensions.ERXEnterpriseObject.updateInverseRelationships=true</code>. * * @property er.extensions.ERXCustomObject.shouldTrimSpaces */ public class ERXCustomObject extends EOCustomObject implements ERXGuardedObjectInterface, ERXGeneratesPrimaryKeyInterface, ERXEnterpriseObject { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; /** holds all subclass related Logger's */ private static final NSMutableDictionary<Class, Logger> classLogs = new NSMutableDictionary<>(); public static boolean shouldTrimSpaces(){ return ERXProperties.booleanForKeyWithDefault("er.extensions.ERXCustomObject.shouldTrimSpaces", false); } /** * Clazz object implementation for ERXCustomObject. See * {@link EOEnterpriseObjectClazz} for more information on this * neat design pattern. * @param <T> */ public static class ERXCustomObjectClazz<T extends EOEnterpriseObject> extends EOEnterpriseObjectClazz<T> { } public String insertionStackTrace = null; protected boolean wasInitialized; private boolean _updateInverseRelationships = ERXGenericRecord.InverseRelationshipUpdater.updateInverseRelationships(); public boolean _setUpdateInverseRelationships(boolean newValue) { boolean old = _updateInverseRelationships; _updateInverseRelationships = newValue; return old; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#getClassLog() */ public Logger getClassLog() { Logger classLog = classLogs.objectForKey(getClass()); if ( classLog == null) { synchronized(classLogs) { classLog = Logger.getLogger(getClass()); classLogs.setObjectForKey(classLog, getClass()); } } return classLog; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#self() */ public ERXEnterpriseObject self(){ return this; } /** * Implementation of {@link ERXGuardedObjectInterface}. This is checked * before the object is deleted in the <code>willDelete</code> method * which is in turn called by {@link ERXEditingContextDelegate}. The default * implementation returns <code>true</code>. * @return true */ public boolean canDelete() { return true; } /** * Implementation of {@link ERXGuardedObjectInterface}. This is checked * before the object is deleted in the <code>willUpdate</code> method * which is in turn called by {@link ERXEditingContextDelegate}. The default * implementation returns <code>true</code>. * @return true */ public boolean canUpdate() { return true; } /** * Implementation of {@link ERXGuardedObjectInterface}. * This is used to work around a bug in EOF that doesn't refresh the relationship in the parent * editingContext for the object. */ public void delete() { editingContext().deleteObject(this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#mightDelete() */ public void mightDelete() { if (tranLogMightDelete.isDebugEnabled()) tranLogMightDelete.debug("Object: " + this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#willDelete() */ public void willDelete() throws NSValidation.ValidationException { if (canDelete() == false) { throw ERXValidationFactory.defaultFactory().createException(this, null, null, "ObjectCannotBeDeletedException"); } if (tranLogWillDelete.isDebugEnabled()) tranLogWillDelete.debug("Object: " + this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#willInsert() */ public void willInsert() { /* Disabling this check by default -- it's causing problems for objects created and deleted in the same transaction */ if (tranLogWillInsert.isDebugEnabled()) { /* check that all the to manies have an array */ for (Enumeration e=toManyRelationshipKeys().objectEnumerator(); e.hasMoreElements();) { String key=(String)e.nextElement(); Object o=storedValueForKey(key); if (o==null || !EOFaultHandler.isFault(o) && o instanceof NSKeyValueCoding.Null) { tranLogWillInsert.error("Found illegal value in to many "+key+" for "+this+": "+o); } } tranLogWillInsert.debug("Object: " + this); } if(shouldTrimSpaces()) trimSpaces(); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#willUpdate() */ public void willUpdate() { /* Disabling this check by default -- it's causing problems for objects created and deleted in the same transaction */ if (tranLogWillUpdate.isDebugEnabled()) { /* check that all the to manies have an array */ for (Enumeration e=toManyRelationshipKeys().objectEnumerator(); e.hasMoreElements();) { String key=(String)e.nextElement(); Object o=storedValueForKey(key); if (o==null || !EOFaultHandler.isFault(o) && o instanceof NSKeyValueCoding.Null) { tranLogWillUpdate.error("Found illegal value in to many "+key+" for "+this+": "+o); } } if (tranLogWillUpdate.isDebugEnabled()) tranLogWillUpdate.debug("Object: " + this + " changes: " + changesFromCommittedSnapshot()); } if(shouldTrimSpaces()) trimSpaces(); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#flushCaches() */ public void flushCaches() {} /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#didDelete(com.webobjects.eocontrol.EOEditingContext) */ public void didDelete(EOEditingContext ec) { if (tranLogDidDelete.isDebugEnabled()) tranLogDidDelete.debug("Object: " + this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#didUpdate() */ public void didUpdate() { if (tranLogDidUpdate.isDebugEnabled()) tranLogDidUpdate.debug("Object: " + this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#didInsert() */ public void didInsert() { if (tranLogDidInsert.isDebugEnabled()) tranLogDidInsert.debug("Object: " + this); //We're going to blow the primaryKey cache: _primaryKey = null; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#willRevert() */ public void willRevert() { if ( tranLogWillRevert.isDebugEnabled() ) tranLogWillRevert.debug("Object: " + this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#didRevert(com.webobjects.eocontrol.EOEditingContext) */ public void didRevert(EOEditingContext ec) { if ( tranLogDidRevert.isDebugEnabled() ) tranLogDidRevert.debug("Object: " + this); flushCaches(); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#addObjectsToBothSidesOfRelationshipWithKey(com.webobjects.foundation.NSArray, java.lang.String) */ public void addObjectsToBothSidesOfRelationshipWithKey(NSArray objects, String key) { if (objects != null && objects.count() > 0) { NSArray objectsSafe = objects instanceof NSMutableArray ? (NSArray)objects.immutableClone() : objects; for (Enumeration e = objectsSafe.objectEnumerator(); e.hasMoreElements();) { EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement(); addObjectToBothSidesOfRelationshipWithKey(eo, key); } } } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#removeObjectsFromBothSidesOfRelationshipWithKey(com.webobjects.foundation.NSArray, java.lang.String) */ public void removeObjectsFromBothSidesOfRelationshipWithKey(NSArray objects, String key) { if (objects != null && objects.count() > 0) { NSArray objectsSafe = objects instanceof NSMutableArray ? (NSArray)objects.immutableClone() : objects; for (Enumeration e = objectsSafe.objectEnumerator(); e.hasMoreElements();) { EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement(); removeObjectFromBothSidesOfRelationshipWithKey(eo, key); } } } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#removeObjectsFromPropertyWithKey(com.webobjects.foundation.NSArray, java.lang.String) */ public void removeObjectsFromPropertyWithKey(NSArray objects, String key) { if (objects != null && objects.count() > 0) { NSArray objectsSafe = objects instanceof NSMutableArray ? (NSArray)objects.immutableClone() : objects; for (Enumeration e = objectsSafe.objectEnumerator(); e.hasMoreElements();) { EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement(); removeObjectFromPropertyWithKey(eo, key); } } } /** * By default, and this should change in the future, all editing contexts that * are created and use ERXEnterpriseObjects or subclasses need to have a delegate * set of instance {@link ERXEditingContextDelegate}. These delegates provide * the augmentation to the regular transaction mechanism, all of the will* methods * plus the flushCaching method. To change the default behaviour set the property: * <b>er.extensions.ERXRaiseOnMissingEditingContextDelegate</b> to false in your * WebObjects.properties file. This method is called when an object is fetched, * updated or inserted. * @param editingContext to check for the correct delegate. * @return if the editing context has the correct delegate set. */ private boolean _checkEditingContextDelegate(EOEditingContext editingContext) { return ERXEditingContextDelegate._checkEditingContextDelegate(editingContext); } /** * Checks the editing context delegate before calling * super's implementation. See the method <code> * _checkEditingContextDelegate</code> for an explanation * as to what this check does. * @param editingContext to be checked to make sure it has the * correct type of delegate set. */ @Override public void awakeFromClientUpdate(EOEditingContext editingContext) { _checkEditingContextDelegate(editingContext); super.awakeFromClientUpdate(editingContext); wasInitialized = true; } /** * Checks the editing context delegate before calling * super's implementation. See the method <code> * _checkEditingContextDelegate</code> for an explanation * as to what this check does. * @param editingContext to be checked to make sure it has the * correct type of delegate set. */ @Override public void awakeFromInsertion(EOEditingContext editingContext) { _checkEditingContextDelegate(editingContext); if (insertionTrackingLog.isDebugEnabled()) { insertionStackTrace = ERXUtilities.stackTrace(); insertionTrackingLog.debug("inserted "+getClass().getName()+" at "+insertionStackTrace); } super.awakeFromInsertion(editingContext); EOGlobalID gid = editingContext.globalIDForObject(this); if (gid.isTemporary()) { init(editingContext); } wasInitialized = true; } /** * used for initialization stuff instead of awakeFromInsertion. * <code>awakeFromInsertions</code> is buggy because if an EO is * deleted and then its EOEditingContext is reverted using 'revert' * for example then EOF will -insert- this EO again in its EOEditingContext * which in turn calls awakeFromInsertion again. * * @param ec the EOEditingContext in which this new EO is inserted */ protected void init(EOEditingContext ec) { } /** * Checks the editing context delegate before calling * super's implementation. See the method <code> * _checkEditingContextDelegate</code> for an explanation * as to what this check does. * @param editingContext to be checked to make sure it has the * correct type of delegate set. */ @Override public void awakeFromFetch(EOEditingContext editingContext) { _checkEditingContextDelegate(editingContext); super.awakeFromFetch(editingContext); wasInitialized = true; } /** * Adds a check to make sure that both the object being added and * this object are in the same editing context. If not then a runtime * exception is thrown instead of getting the somewhat cryptic NSInternalInconsistency * excpetion that is thrown when you attempt to save changes to the database. * @param eo enterprise object to be added to the relationship * @param key relationship to add the object to. */ @Override public void addObjectToBothSidesOfRelationshipWithKey(EORelationshipManipulation eo, String key) { if (eo!=null && ((EOEnterpriseObject)eo).editingContext()!=editingContext() && !(editingContext() instanceof EOSharedEditingContext) && !(((EOEnterpriseObject)eo).editingContext() instanceof EOSharedEditingContext)) { if (((EOEnterpriseObject)eo).editingContext()==null || editingContext()==null) { if(editingContext()==null) throw new RuntimeException("******** Attempted to link to EOs through " +key+" when one of them was not in an editing context: " +this+":"+editingContext()+" and "+eo+ ":" + ((EOEnterpriseObject)eo).editingContext()); } else { throw new RuntimeException("******** Attempted to link to EOs through "+key+" in different editing contexts: "+this+" and "+eo); } } super.addObjectToBothSidesOfRelationshipWithKey(eo,key); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#primaryKey() */ protected String _primaryKey = null; public String primaryKey() { if(_primaryKey == null) { _primaryKey = ERXEOControlUtilities.primaryKeyStringForObject(this); } return _primaryKey; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#rawPrimaryKeyInTransaction() */ public Object rawPrimaryKeyInTransaction() { Object result = rawPrimaryKey(); if (result == null) { NSDictionary<String, Object> pk = rawPrimaryKeyDictionary(false); NSArray<String> primaryKeyAttributeNames = primaryKeyAttributeNames(); result = ERXArrayUtilities.valuesForKeyPaths(pk, primaryKeyAttributeNames); if(((NSArray)result).count() == 1) result = ((NSArray)result).lastObject(); } return result; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#primaryKeyInTransaction() */ public String primaryKeyInTransaction() { return ERXEOControlUtilities._stringForPrimaryKey(rawPrimaryKeyInTransaction()); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#rawPrimaryKey() */ public Object rawPrimaryKey() { return ERXEOControlUtilities.primaryKeyObjectForObject(this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#encryptedPrimaryKey() */ public String encryptedPrimaryKey() { String pk = ERXEOControlUtilities.primaryKeyStringForObject(this); return pk==null ? null : ERXCrypto.crypterForAlgorithm(ERXCrypto.BLOWFISH).encrypt(pk); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#foreignKeyForRelationshipWithKey(java.lang.String) */ public Object foreignKeyForRelationshipWithKey(String rel) { NSDictionary d=EOUtilities.destinationKeyForSourceObject(editingContext(), this, rel); return d != null && d.count()>0 ? d.allValues().objectAtIndex(0) : null; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#primaryKeyAttributeNames() */ public NSArray primaryKeyAttributeNames() { EOEntity entity = ERXEOAccessUtilities.entityNamed(editingContext(), entityName()); return entity.primaryKeyAttributeNames(); } /** caches the primary key dictionary for the given object */ private NSDictionary<String, Object> _primaryKeyDictionary; /** * Implementation of the interface {@link ERXGeneratesPrimaryKeyInterface}. * This implementation operates in the following fashion. If it is called * passing in 'false' and it has not yet been saved to the database, meaning * this object does not yet have a primary key assigned, then it will have the * adaptor channel generate a primary key for it. Then when the object is saved * to the database it will use the previously generated primary key instead of * having the adaptor channel generate another primary key. If 'true' is passed in * then this method will either return the previously generated primaryKey * dictionary or null if it does not have one. Typically you should only call * this method with the 'false' parameter seeing as unless you are doing something * really funky you won't be dealing with this object when it is in the middle of * a transaction. The delegate {@link ERXDatabaseContextDelegate} is the only class * that should be calling this method and passing in 'true'. * @param inTransaction boolean flag to tell the object if it is currently in the * middle of a transaction. * @return primary key dictionary for the current object, if the object does not have * a primary key assigned yet and is not in the middle of a transaction then * a new primary key dictionary is created, cached and returned. */ @Override public NSDictionary<String, Object> rawPrimaryKeyDictionary(boolean inTransaction) { if(_primaryKeyDictionary == null) { if (!inTransaction) { Object rawPK = rawPrimaryKey(); if (rawPK != null) { if (log.isDebugEnabled()) log.debug("Got raw key: "+ rawPK); NSArray<String> primaryKeyAttributeNames = primaryKeyAttributeNames(); _primaryKeyDictionary = new NSDictionary<>(rawPK instanceof NSArray ? (NSArray)rawPK : new NSArray<>(rawPK), primaryKeyAttributeNames); } else { if (log.isDebugEnabled()) log.debug("No raw key, trying single key"); _primaryKeyDictionary = ERXEOControlUtilities.newPrimaryKeyDictionaryForObject(this); } } } return _primaryKeyDictionary; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#committedSnapshotValueForKey(java.lang.String) */ public Object committedSnapshotValueForKey(String key) { NSDictionary snapshot = editingContext().committedSnapshotForObject(this); return snapshot != null ? snapshot.objectForKey(key) : null; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#localInstanceOf(com.webobjects.eocontrol.EOEnterpriseObject) */ public EOEnterpriseObject localInstanceOf(EOEnterpriseObject eo) { return ERXEOControlUtilities.localInstanceOfObject(editingContext(), eo); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#localInstanceIn(com.webobjects.eocontrol.EOEnterpriseObject) */ public EOEnterpriseObject localInstanceIn(EOEditingContext ec) { return ERXEOControlUtilities.localInstanceOfObject(ec, this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#localInstancesOf(com.webobjects.foundation.NSArray) */ @SuppressWarnings("unchecked") public NSArray localInstancesOf(NSArray eos) { return ERXEOControlUtilities.localInstancesOfObjects(editingContext(), eos); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#changesFromCommittedSnapshot() */ public NSDictionary changesFromCommittedSnapshot() { return changesFromSnapshot(editingContext().committedSnapshotForObject(this)); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#parentObjectStoreIsObjectStoreCoordinator() */ public boolean parentObjectStoreIsObjectStoreCoordinator() { return editingContext().parentObjectStore() instanceof EOObjectStoreCoordinator; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#refetchObjectFromDBinEditingContext(com.webobjects.eocontrol.EOEditingContext) */ public ERXEnterpriseObject refetchObjectFromDBinEditingContext(EOEditingContext ec){ EOEntity entity = ERXEOAccessUtilities.entityNamed(ec, entityName()); EOQualifier qual = entity.qualifierForPrimaryKey(rawPrimaryKeyDictionary(false)); EOFetchSpecification fetchSpec = new EOFetchSpecification(entityName(), qual, null); fetchSpec.setRefreshesRefetchedObjects(true); NSArray results = ec.objectsWithFetchSpecification(fetchSpec); ERXEnterpriseObject freshObject = null; if(results.count()>0){ freshObject = (ERXEnterpriseObject)results.objectAtIndex(0); } return freshObject; } /** * Overrides the EOGenericRecord's implementation to * provide a slightly less verbose output. A typical * output for an object mapped to the class com.foo.User * with a primary key of 50 would look like: * <com.foo.User pk:"50"> * EOGenericRecord's implementation is preserved in the * method <code>toLongString</code>. To restore the original * verbose logging in your subclasses override this method and * return toLongString. * @return much less verbose description of an enterprise * object. */ @Override public String toString() { String pk = primaryKey(); pk = (pk == null) ? "null" : pk; return "<" + getClass().getName() + " pk:\""+ pk + "\">"; } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#toLongString() */ public String toLongString() { return super.toString(); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#trimSpaces() */ public void trimSpaces() { ERXEOControlUtilities.trimSpaces(this); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#isDeletedEO() */ public boolean isDeletedEO() { if (log.isDebugEnabled()) { log.debug("editingContext() = " + editingContext() + " this object: " + this); } // HACK AK: using private API here EOGlobalID gid = __globalID(); boolean isDeleted = (editingContext() == null && (gid != null && !gid.isTemporary())); return isDeleted || (editingContext() != null && editingContext().deletedObjects().containsObject(this)); } /* (non-Javadoc) * @see er.extensions.ERXEnterpriseObject#isNewObject() */ public boolean isNewObject() { return ERXEOControlUtilities.isNewObject(this); } /** * Overrides the default validation mechanisms to provide * a few checks before invoking super's implementation, * which incidently just invokes validateValueForKey on the * object's class description. The class description for this * object should be an {@link ERXEntityClassDescription} or subclass. * It is that class that provides the hooks to convert model * throw validation exceptions into {@link ERXValidationException} * objects. * @param value to be validated for a given attribute or relationship * @param key corresponding to an attribute or relationship * @throws NSValidation.ValidationException if the value fails validation * @return the validated value */ @Override public Object validateValueForKey(Object value, String key) throws NSValidation.ValidationException { if (validation.isDebugEnabled()) validation.debug("ValidateValueForKey on eo: " + this + " value: " + value + " key: " + key); if (key==null) // better to raise before calling super which will crash throw new RuntimeException("validateValueForKey called with null key on "+this); Object result=null; try { result=super.validateValueForKey(value,key); EOClassDescription cd = classDescription(); if(cd instanceof ERXEntityClassDescription) { ((ERXEntityClassDescription)cd).validateObjectWithUserInfo(this, value, "validateForKey." + key, key); } } catch (ERXValidationException e) { throw e; } catch (NSValidation.ValidationException e) { if (e.key() == null || e.object() == null || (e.object() != null && !(e.object() instanceof EOEnterpriseObject))) e = new NSValidation.ValidationException(e.getMessage(), this, key); if(validationException.isDebugEnabled()) { validationException.debug("Exception: " + e.getMessage() + " raised while validating object: " + this + " class: " + getClass() + " pKey: " + primaryKey(), e); } throw e; } catch (RuntimeException e) { log.error("**** During validateValueForKey "+key, e); throw e; } return result; } /** * This method performs a few checks before invoking * super's implementation. * * @throws NSValidation.ValidationException if the object does not * pass validation for saving to the database. */ @Override public void validateForSave( ) throws NSValidation.ValidationException { // This condition shouldn't ever happen, but it does ;) // CHECKME: This was a 4.5 issue, not sure if this one has been fixed yet. if (editingContext() != null && editingContext().deletedObjects().containsObject(this)) { validation.warn("Calling validate for save on an eo: " + this + " that has been marked for deletion!"); } super.validateForSave(); } /** * Calls up validateForInsert() on the class description if it supports it. * @throws NSValidation.ValidationException if the object does not * pass validation for saving to the database. */ @Override public void validateForInsert() throws NSValidation.ValidationException { EOClassDescription cd = classDescription(); if(cd instanceof ERXEntityClassDescription) { ((ERXEntityClassDescription)cd).validateObjectForInsert(this); } super.validateForInsert(); } /** * Calls up validateForUpdate() on the class description if it supports it. * @throws NSValidation.ValidationException if the object does not * pass validation for saving to the database. */ @Override public void validateForUpdate() throws NSValidation.ValidationException { EOClassDescription cd = classDescription(); if(cd instanceof ERXEntityClassDescription) { ((ERXEntityClassDescription)cd).validateObjectForUpdate(this); } super.validateForUpdate(); } /** * Calls up validateForUpdate() on the class description if it supports it. * @throws NSValidation.ValidationException if the object does not * pass validation for saving to the database. */ @Override public void validateForDelete() throws NSValidation.ValidationException { EOClassDescription cd = classDescription(); if(cd instanceof ERXEntityClassDescription) { ((ERXEntityClassDescription)cd).validateObjectForDelete(this); } super.validateForDelete(); } /** * Overridden to support two-way relationship setting. */ @Override protected void includeObjectIntoPropertyWithKey(Object o, String key) { if (_updateInverseRelationships) { ERXGenericRecord.InverseRelationshipUpdater.includeObjectIntoPropertyWithKey(this, o, key); } super.includeObjectIntoPropertyWithKey(o, key); } /** * Overridden to support two-way relationship setting. */ @Override protected void excludeObjectFromPropertyWithKey(Object o, String key) { if (_updateInverseRelationships) { ERXGenericRecord.InverseRelationshipUpdater.excludeObjectFromPropertyWithKey(this, o, key); } super.excludeObjectFromPropertyWithKey(o, key); } @Override public void takeValueForKey(Object value, String key) { super.takeValueForKey(value, key); } @Override public void takeStoredValueForKey(Object value, String key) { if (_updateInverseRelationships) { ERXGenericRecord.InverseRelationshipUpdater.takeStoredValueForKey(this, value, key); } super.takeStoredValueForKey(value, key); } // Debugging aids -- turn off during production // These methods are used to catch the classic mistake of: // public String foo() { return (String)valueForKey("foo"); } // where foo is not a property key /* public Object storedValueForKey(String key) { // FIXME: turn this off during production if (!allPropertyKeys().containsObject(key)) throw new RuntimeException("********* Tried to access storedValueForKey on "+entityName()+" on a non class property: "+key); Object value = super.storedValueForKey(key); return value; } public void takeStoredValueForKey(Object value, String key) { // FIXME: turn this off during production if (!allPropertyKeys().containsObject(key)) { throw new RuntimeException("********* Tried to takeStoredValueForKey on "+entityName()+" on a non class property: "+key); } super.takeStoredValueForKey(value,key); } */ public static boolean usesDeferredFaultCreation() { return true; } }