3474 lines
167 KiB
Plaintext
3474 lines
167 KiB
Plaintext
|
|
package com.foundation.attribute;
|
||
|
|
|
||
|
|
import java.lang.ref.Reference;
|
||
|
|
import java.lang.ref.SoftReference;
|
||
|
|
|
||
|
|
import com.common.thread.IRunnable;
|
||
|
|
import com.common.thread.Monitor;
|
||
|
|
import com.common.util.*;
|
||
|
|
import com.common.util.optimized.IntHashSet;
|
||
|
|
import com.common.comparison.Comparator;
|
||
|
|
import com.common.debug.*;
|
||
|
|
import com.common.orb.Orb;
|
||
|
|
import com.foundation.metadata.*;
|
||
|
|
import com.foundation.util.ITrackedCollection;
|
||
|
|
import com.foundation.common.AttributeBinding;
|
||
|
|
import com.foundation.common.EntityMetadata;
|
||
|
|
import com.foundation.common.IEntity;
|
||
|
|
import com.foundation.common.MetadataContainer;
|
||
|
|
import com.foundation.event.*;
|
||
|
|
import com.foundation.event.model.CollectionBinding;
|
||
|
|
import com.foundation.event.model.ModelListener;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2002,2006<p>
|
||
|
|
* This class abstracts the attribute management for classes that use managed attributes.
|
||
|
|
* All managed attributes should be declared in the class's static initializer before accessing the values via the class's AttributeSupport instance.
|
||
|
|
* <p>This class manages the attribute values, changes, change flagging, creating change sets, and merging change sets.</p>
|
||
|
|
* <p>Note: The change flags (and original value references) are not serialized. Change flags should be cleared ASAP to avoid unnecessary memory usage.</p>
|
||
|
|
* @see #register(Class, String)
|
||
|
|
* @see com.foundation.model.Model
|
||
|
|
*/
|
||
|
|
public class AttributeSupport extends ReflectObjectSupport implements com.common.io.IExternalizable, java.io.Externalizable, CloneContext.IPostCloneCleanupListener {
|
||
|
|
public static final String ATTRIBUTE_CHANGED_EXTENSION = "Changed";
|
||
|
|
public static final int CONTEXT_UNTRUSTED = 0x00;
|
||
|
|
public static final int CONTEXT_TRUSTED = 0x01;
|
||
|
|
public static final int CONTEXT_UPDATE = 0x02;
|
||
|
|
|
||
|
|
public static final int OPTION_REFERENCED = AttributeMetadata.OPTION_REFERENCED;
|
||
|
|
public static final int OPTION_PART_OF = AttributeMetadata.OPTION_PART_OF;
|
||
|
|
public static final int OPTION_LAZY = AttributeMetadata.OPTION_LAZY;
|
||
|
|
public static final int OPTION_WEAK = AttributeMetadata.OPTION_WEAK;
|
||
|
|
public static final int OPTION_COLLECTION = AttributeMetadata.OPTION_COLLECTION;
|
||
|
|
public static final int OPTION_MAPPED = AttributeMetadata.OPTION_MAPPED;
|
||
|
|
public static final int OPTION_TRANSIENT = AttributeMetadata.OPTION_TRANSIENT;
|
||
|
|
public static final int OPTION_NO_REFLECT = AttributeMetadata.OPTION_NO_REFLECT;
|
||
|
|
public static final int OPTION_CALCULATED = AttributeMetadata.OPTION_CALCULATED;
|
||
|
|
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_PART_OF_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_PART_OF_ATTRIBUTES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_CALCULATED_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_CALCULATED_ATTRIBUTES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_WEAK_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_WEAK_ATTRIBUTES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_COLLECTION_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_COLLECTION_ATTRIBUTES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_BACK_REFERENCES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_BACK_REFERENCES;
|
||
|
|
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_BOUND_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_BOUND_ATTRIBUTES;
|
||
|
|
|
||
|
|
/** An object used to identify an attribute whose value is actually null, versus not having been set yet. */
|
||
|
|
private static final NullValue NULL_VALUE = new NullValue();
|
||
|
|
/** An object used to identify an attribute whose value is not set yet, but when it is set it must be recalculated. */
|
||
|
|
private static final RecalculateValue RECALCULATE_VALUE = new RecalculateValue();
|
||
|
|
/** An object used to identify that an attribute is actively being loaded. If a thread encounters this value for an attribute, it will suspend its lock and will wait on this support object until notified that the load has finished. */
|
||
|
|
private static final Object VALUE_LOADING = new Object();
|
||
|
|
/** Used to identify a value that was not lazy loaded. */
|
||
|
|
private static final Object NOT_LAZY_LOADED = new Object();
|
||
|
|
/** Whether the system should be checking access by threads to validate that they have the appropriate locks. This is for debugging only, as it will slow the application considerably. */
|
||
|
|
private static boolean isCheckingLocks = false;
|
||
|
|
/** A thread local that identifies threads which have suspended or delayed the listening temporarily. */
|
||
|
|
private static final ThreadLocal calculatedAttributeSuspendData = new ThreadLocal();
|
||
|
|
|
||
|
|
/** The access object that the supported object can use to access normally protected methods. */
|
||
|
|
private AttributeSupportAccess access = new AttributeSupportAccess();
|
||
|
|
/** The entity that is being supported. */
|
||
|
|
private IEntity supportedObject = null;
|
||
|
|
/** The attribute values before any changes. The value for an attribute will be null if it has not yet been loaded, NULL_VALUE if it is actually null, or a SoftReference if the attribute is marked as weak. */
|
||
|
|
private Object[] currentAttributeValues = null;
|
||
|
|
/** The attribute values as altered by the user. These values override the current atribute values. The value will be null if the attribute is not altered, otherwise it will be the value the attribute has been assigned. */
|
||
|
|
private Object[] alteredAttributeValues = null;
|
||
|
|
/** The custom lazy load handlers for the supported object. The handler corresponds to the attribute lazily loaded. Handlers will be invoked if available and if the attribute is lazily loaded (or if it has a default value) before the standard lazy loading or default value is used. */
|
||
|
|
private ICustomLazyLoadHandler[] customLazyLoadHandlers = null;
|
||
|
|
/** The head of a linked list of change listeners that must be notified inline of changes to attribute values. These listeners get passed the old and new value unlike traditional event listeners. */
|
||
|
|
private transient LocalAttributeChangeListener[] localChangeListeners = null;
|
||
|
|
/** A counter used to determine whether the virtual object has been changed. The counter will be one if this object has changed, and will increment by one for each part-of object directly referenced by this object that has virtual object changes (it may not be changed, but it may have one or more part-of references which have been changed). */
|
||
|
|
private transient int virtualObjectChangeCounter = 0;
|
||
|
|
/** A flag that is used to track whether the object may have been altered. This is intended to be less accuate than hasChanged and the added and removed values collections. The intended use is to improve view validation by only validating objects that may have been altered. */
|
||
|
|
private transient boolean altered = false;
|
||
|
|
/** A counter that is used to track whether the virtual object may have been altered. This will be 1 if this object is altered, +1 per part-of object that is altered. */
|
||
|
|
private int virtualObjectAlteredCounter = 0;
|
||
|
|
/** Whether the object has been modified since the last time the change flags were set. Modifications that are subsiquently undone will reset this flag. */
|
||
|
|
private transient boolean hasChanged = false;
|
||
|
|
/** Whether the object has been modified and needs updating in the repository from which it came. This is always false for new objects. */
|
||
|
|
private transient boolean canUpdate = false;
|
||
|
|
/** References the repository identifier for the repository from which this object was read, or null if this object was not read from a repository or if the object is not mapped to any repository. */
|
||
|
|
private transient Object repositoryIdentifier = null;
|
||
|
|
/** References the type metadata for the repository from which this object was read or created, or null if this object was not read from a repository or created in a repository. */
|
||
|
|
private transient TypeRepositoryMetadata typeRepositoryMetadata = null;
|
||
|
|
/** The support object that manages event registrations and firings for this attribute support. */
|
||
|
|
private transient EventSupport eventSupport = null;
|
||
|
|
/** The metadata for the supported object which is used to get extended attribute information. */
|
||
|
|
private transient TypeMetadata typeMetadata = null;
|
||
|
|
/** A collection of AttributeMetadata references for attributes that are bound to the entity that this supported entity is part of. So if Address is part of Contact and Address.CONTACT is bound to a referencing Contact type, then the registerBoundAttributes(..) is called when the Address becomes part of the Contact. This will populate this collection for the bound attributes associated with Contact.class. This collection then simplifies the cleanup if the object is no longer part of the Contact. */
|
||
|
|
private transient IList partOfAttributeBindings;
|
||
|
|
/** If greater than zero, don't use weak or soft references. This allows the clone and reflect operations to pre-lazy load and de-reference data prior to cloning or reflecting the attributes. */
|
||
|
|
private transient int suspendWeakReferencesCount = 0;
|
||
|
|
/** A collection of the model listeners used by the calculated attributes. This is only intended to maintain references to the listeners so they are not GC'd while this attribute support exists. */
|
||
|
|
private transient ModelListener[] calculatedAttributeListeners = null;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2007<p>
|
||
|
|
*/
|
||
|
|
public class AttributeSupportAccess {
|
||
|
|
/**
|
||
|
|
* AttributeSupportAccess constructor.
|
||
|
|
*/
|
||
|
|
protected AttributeSupportAccess() {
|
||
|
|
}//AttributeSupportAccess()//
|
||
|
|
/**
|
||
|
|
* Sets the value associated with the specified attribute.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param value The value that should be associated with this attribute.
|
||
|
|
* @param changeContext The context under which this attribute is being changed.
|
||
|
|
* @return Whether the value was changed. This will only be false if the new value and old value are identical and no change was required.
|
||
|
|
*/
|
||
|
|
public boolean setAttributeValue(int attributeNumber, Object value, byte changeContext) {
|
||
|
|
return AttributeSupport.this.setAttributeValue(attributeNumber, value, changeContext);
|
||
|
|
}//setAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Clears the part of entity field without any of the usual processing for the purpose of synchronizing a reflection.
|
||
|
|
*/
|
||
|
|
public void internalSetPartOfEntity(IEntity partOfEntity) {
|
||
|
|
AttributeSupport.this.internalSetPartOfEntity(partOfEntity);
|
||
|
|
}//internalClearPartOfEntity()//
|
||
|
|
/**
|
||
|
|
* Gets the entity the collection is part of.
|
||
|
|
* @return The entity that this collection is a part of. This may be null.
|
||
|
|
*/
|
||
|
|
public IEntity getPartOfEntity() {
|
||
|
|
return AttributeSupport.this.getPartOfEntity();
|
||
|
|
}//getPartOfEntity()//
|
||
|
|
/**
|
||
|
|
* Sets the entity the collection is part of.
|
||
|
|
* @param partOfEntity The entity that this collection is a part of. This may be null.
|
||
|
|
*/
|
||
|
|
public void setPartOfEntity(IEntity partOfEntity) {
|
||
|
|
AttributeSupport.this.setPartOfEntity(partOfEntity);
|
||
|
|
}//setPartOfEntity()//
|
||
|
|
/**
|
||
|
|
* Gets the count of times the part-of entity has been set to the current value.
|
||
|
|
* @return The counter for setting the part of entity.
|
||
|
|
*/
|
||
|
|
public int getPartOfEntityCounter() {
|
||
|
|
return AttributeSupport.this.getPartOfEntityCounter();
|
||
|
|
}//getPartOfEntityCounter()//
|
||
|
|
/**
|
||
|
|
* Sets the count of times the part-of entity has been set to the current value.
|
||
|
|
* @param partOfEntityCounter The counter for setting the part of entity.
|
||
|
|
*/
|
||
|
|
public void setPartOfEntityCounter(int partOfEntityCounter) {
|
||
|
|
AttributeSupport.this.setPartOfEntityCounter(partOfEntityCounter);
|
||
|
|
}//setPartOfEntityCounter()//
|
||
|
|
}//AttributeSupportAccess//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2006<p>
|
||
|
|
* Defines a listener for changes to attribute values.
|
||
|
|
*/
|
||
|
|
public interface IAttributeChangeListener {
|
||
|
|
/**
|
||
|
|
* Called inline when the listened for attribute changes.
|
||
|
|
* @param oldValue
|
||
|
|
* @param newValue
|
||
|
|
*/
|
||
|
|
public void attributeChanged(Object oldValue, Object newValue);
|
||
|
|
}//IAttributeChangeListener//
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2006<p>
|
||
|
|
* A node in the linked list of attribute change listeners.
|
||
|
|
* This class is for internal use only.
|
||
|
|
*/
|
||
|
|
private static final class LocalAttributeChangeListener {
|
||
|
|
public LocalAttributeChangeListener next = null;
|
||
|
|
public IAttributeChangeListener listener = null;
|
||
|
|
|
||
|
|
public LocalAttributeChangeListener(LocalAttributeChangeListener next, IAttributeChangeListener listener) {
|
||
|
|
this.next = next;
|
||
|
|
this.listener = listener;
|
||
|
|
}//LocalAttributeChangeListener()//
|
||
|
|
}//LocalAttributeChangeListener//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2003,2006<p>
|
||
|
|
* Encapsulates a collection of specific attributes and their values at a specific instant in time.
|
||
|
|
*/
|
||
|
|
public static final class AttributeState {
|
||
|
|
private AttributeSupport support = null;
|
||
|
|
private Object[] currentAttributeValues = null;
|
||
|
|
private Object[] alteredAttributeValues = null;
|
||
|
|
private boolean hasChanged = false;
|
||
|
|
private boolean canUpdate = false;
|
||
|
|
private Object repositoryIdentifier = null;
|
||
|
|
private TypeRepositoryMetadata typeRepositoryMetadata = null;
|
||
|
|
|
||
|
|
public AttributeState() {
|
||
|
|
}//AttributeState()//
|
||
|
|
private AttributeState(AttributeSupport support) {
|
||
|
|
this.support = support;
|
||
|
|
this.currentAttributeValues = (Object[]) support.currentAttributeValues.clone();
|
||
|
|
this.alteredAttributeValues = (Object[]) support.alteredAttributeValues.clone();
|
||
|
|
this.hasChanged = support.hasChanged;
|
||
|
|
this.canUpdate = support.canUpdate;
|
||
|
|
this.repositoryIdentifier = support.repositoryIdentifier;
|
||
|
|
this.typeRepositoryMetadata = support.typeRepositoryMetadata;
|
||
|
|
|
||
|
|
//Convert the soft references to hard references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
if(currentAttributeValues[index] instanceof SoftReference) {
|
||
|
|
currentAttributeValues[index] = ((SoftReference) currentAttributeValues[index]).get();
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//AttributeState()//
|
||
|
|
/**
|
||
|
|
* Restores the state of the object's attributes.
|
||
|
|
*/
|
||
|
|
public void restore() {
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object oldValue = support.alteredAttributeValues[index] == null ? support.currentAttributeValues[index] : support.alteredAttributeValues[index];
|
||
|
|
|
||
|
|
if((support.allowWeakReferences()) && (currentAttributeValues[index] != null) && (!(currentAttributeValues[index] instanceof NullValue)) && (support.getIsAttributeWeak(index))) {
|
||
|
|
support.currentAttributeValues[index] = new SoftReference(currentAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
support.currentAttributeValues[index] = currentAttributeValues[index];
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
support.alteredAttributeValues[index] = alteredAttributeValues[index];
|
||
|
|
support.fireUpdates(index, oldValue, support.alteredAttributeValues[index] == null ? support.currentAttributeValues[index] : support.alteredAttributeValues[index], true, 0);
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
support.hasChanged = hasChanged;
|
||
|
|
support.canUpdate = canUpdate;
|
||
|
|
support.repositoryIdentifier = repositoryIdentifier;
|
||
|
|
support.typeRepositoryMetadata = typeRepositoryMetadata;
|
||
|
|
}//restore()//
|
||
|
|
}//AttributeState//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2003,2006<p>
|
||
|
|
* Encapsulates a collection of specific attributes and their values at a specific instant in time.
|
||
|
|
*/
|
||
|
|
public static final class AttributeCollection implements com.common.io.IExternalizable, java.io.Externalizable {
|
||
|
|
/** The class whose attributes are collected. */
|
||
|
|
Class type = null;
|
||
|
|
/** A collection of attribute numbers, one for each attribute whose value is being collected. */
|
||
|
|
int[] attributeNumbers = null;
|
||
|
|
/** A collection of attribute values corresponding to the attribute names. */
|
||
|
|
Object[] attributeValues = null;
|
||
|
|
/** A collection of flags that will be used when serializing to ignore some or all attributes. The flag is indexed by the attribute number it enables/disables. */
|
||
|
|
boolean[] attributeMask = null;
|
||
|
|
/** The number of attributes represented in the collection. Note that this may be different from the size of the arrays due to serialization anomolies. */
|
||
|
|
int attributeCount = 0;
|
||
|
|
|
||
|
|
public AttributeCollection() {
|
||
|
|
}//AttributeCollection()//
|
||
|
|
public AttributeCollection(int[] attributeNumbers, Object[] attributeValues, Class type) {
|
||
|
|
this.attributeNumbers = attributeNumbers;
|
||
|
|
this.attributeValues = attributeValues;
|
||
|
|
this.attributeCount = attributeNumbers.length;
|
||
|
|
this.type = type;
|
||
|
|
}//AttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Replaces values matching the interface class in the attribute collection with proxies to the values.
|
||
|
|
* Objects not proxied will be serialized if the attribute collection is serialized.
|
||
|
|
* <p>The intended purpose is to allow users to prepare the attribute collection for serialization.</p>
|
||
|
|
* @param interfaceClass The class of object to be proxied (also used as the proxy interface).
|
||
|
|
*/
|
||
|
|
public void proxyValues(Class interfaceClass) {
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if((attributeValues[index] != null) && !(attributeValues[index] instanceof NullValue) && (interfaceClass.isAssignableFrom(attributeValues[index].getClass()))) {
|
||
|
|
attributeValues[index] = Orb.getProxy(attributeValues[index], interfaceClass);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//proxyValues()//
|
||
|
|
/**
|
||
|
|
* Creates reflection data for all IReflectable values.
|
||
|
|
* <p>Notes: <br>
|
||
|
|
* This method should only be called after synchronizing a reflection with its reflected object in order to generate a result for the reflection's reflection context.</br>
|
||
|
|
* The method adds each reflectable object (that was passed in the synchronization) to the reflection creation context for later reflection data bundling.
|
||
|
|
* </p>
|
||
|
|
* @param createReflectDataContext The context for creating the reflection data.
|
||
|
|
* @param typeMetadata The metadata for the class the attribute collection belongs to.
|
||
|
|
*/
|
||
|
|
public void reflectValues(CreateReflectDataContext createReflectDataContext, TypeMetadata typeMetadata) {
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if(attributeValues[index] instanceof IReflectable) {
|
||
|
|
createReflectDataContext.include((IReflectable) attributeValues[index]);
|
||
|
|
|
||
|
|
if(typeMetadata.getAttributeMetadata(index).isPartOf() && createReflectDataContext.isSynchronizationResult()) {
|
||
|
|
//Request the reflectable object to recursively search for part-of IReflectable references used to determine what to reflect as a result of the synchronization of data (to the synchronizing reflection context only).//
|
||
|
|
((IReflectable) attributeValues[index]).zzrCollectPostSynchronizeReflectables(attributeValues[index], createReflectDataContext);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//reflectValues()//
|
||
|
|
/**
|
||
|
|
* Converts any reflections created under the given context to be the reflected reference.
|
||
|
|
* <p>This is handy prior to synchronzing a reflection which may reference other reflections since we want to send the reflected reference, not the reflection when synchronizing.</p>
|
||
|
|
* <p>Warning: This will remove part-of metadata for part-of non-reflection references in this reflection context.</p>
|
||
|
|
* @param reflectionContext The context under which the reflections we will convert were created.
|
||
|
|
* @param destinationContext The context that the data is being migrated to. This may be null if the desination is not a reflection context, but instead the live data, or if the reflection context is remote (the data will be copied).
|
||
|
|
* @param typeMetadata The metadata for the class of object this attribute collection is tied to.
|
||
|
|
*/
|
||
|
|
public void dereflect(ReflectionContext reflectionContext, ReflectionContext destinationContext, TypeMetadata typeMetadata) {
|
||
|
|
//Convert reflectable attribute values so they can be copied to the
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if(attributeValues[index] instanceof IReflectable) {
|
||
|
|
IReflectable reflectable = (IReflectable) attributeValues[index];
|
||
|
|
|
||
|
|
if(reflectable.zzrIsReflection(reflectable, reflectionContext)) {
|
||
|
|
attributeValues[index] = reflectable.getReflected();
|
||
|
|
|
||
|
|
//If the value is not a reflection in the destination context then make it one.//
|
||
|
|
if((destinationContext != null) && (!((IReflectable) attributeValues[index]).zzrIsReflection(attributeValues[index], destinationContext))) {
|
||
|
|
attributeValues[index] = destinationContext.createReflection((IReflectable) attributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else if(reflectable.isReflection()) {
|
||
|
|
//If the collection values are part-of the parent then this is an obvious error.//
|
||
|
|
if(typeMetadata.getAttributeMetadata(attributeNumbers[index]).isPartOf()) {
|
||
|
|
Debug.log(new RuntimeException(), "Error: Invalid reflection state: one of the attributes is a reflection in another reflection context - this is not allowed.");
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Copy as is... is this right?//
|
||
|
|
}//else if//
|
||
|
|
else if((((IReflectable) attributeValues[index]).isReflection()) && (Orb.isLocal(((IReflectable) attributeValues[index]).getReflected()))) {
|
||
|
|
//Note: Non-local reflections not of this context are allows since they are assumed to be non-view based reflections.//
|
||
|
|
Debug.log(new RuntimeException(), "Error: Detected a reflection in the wrong context.");
|
||
|
|
}//else if//
|
||
|
|
else if(typeMetadata.getAttributeMetadata(attributeNumbers[index]).isPartOf()) {
|
||
|
|
if(attributeValues[index] instanceof IReflectable) {
|
||
|
|
//Clone the new model - don't clone any reflections.//
|
||
|
|
Object clone = ((IReflectable) attributeValues[index]).cloneObject(new MetadataContainer(MetadataContainer.FLAG_EXCLUDE_REFLECTIONS));
|
||
|
|
|
||
|
|
//Call pre-synchronize on the clone to convert any referenced reflections into references to the reflected object, and to link the cloned objects to the clones such that the generated reflection data (as a result of a successful synchronization) can use the cloned objects as the reflections (allowing reflections of the cloned objects to remain useable, and in the event of a failed synchronization they can re-attempt synchronization later).//
|
||
|
|
((IReflectable) clone).zzrReflectionPreSynchronize(clone, reflectionContext, destinationContext, null, (IReflectable) attributeValues[index], reflectionContext);
|
||
|
|
//Replace the attribute value in this collection of attributes to be synchronized with the clone, so the value in the current reflection context is untouched.//
|
||
|
|
attributeValues[index] = clone;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
Debug.log(new RuntimeException("Unable to properly handle a non-IReflectable object that is referenced as part-of."));
|
||
|
|
}//else//
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
//TODO: Is this an error? - Also exists in ReflectCollectionSupport.getSynchronizationData()//
|
||
|
|
//Do nothing - copy reference as is.//
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//dereflect()//
|
||
|
|
/**
|
||
|
|
* Gets the number of attributes not filtered by the attribute mask.
|
||
|
|
*/
|
||
|
|
public int getFilteredCount() {
|
||
|
|
int count = 0;
|
||
|
|
|
||
|
|
if(attributeMask != null) {
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if((attributeNumbers[index] < attributeMask.length) && (attributeMask[attributeNumbers[index]])) {
|
||
|
|
count++;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
count = attributeCount;
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
return count;
|
||
|
|
}//getFilteredCount()//
|
||
|
|
/**
|
||
|
|
* Gets a new attribute collection that does not include any of the filtered attributes.
|
||
|
|
* If the no attributes needed to be filtered then the new collection will be this collection.
|
||
|
|
* @return This collection, or if filtering was necessary, a new collection.
|
||
|
|
*/
|
||
|
|
public AttributeCollection getFilteredCollection() {
|
||
|
|
int filteredCount = getFilteredCount();
|
||
|
|
AttributeCollection result = this;
|
||
|
|
|
||
|
|
if(filteredCount != attributeCount) {
|
||
|
|
int index = 0;
|
||
|
|
|
||
|
|
result = new AttributeCollection();
|
||
|
|
result.attributeCount = filteredCount;
|
||
|
|
result.type = type;
|
||
|
|
result.attributeNumbers = new int[filteredCount];
|
||
|
|
result.attributeValues = new Object[filteredCount];
|
||
|
|
|
||
|
|
for(int attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||
|
|
if((attributeNumbers[attributeIndex] < attributeMask.length) && (attributeMask[attributeNumbers[attributeIndex]])) {
|
||
|
|
result.attributeNumbers[index] = attributeNumbers[attributeIndex];
|
||
|
|
result.attributeValues[index++] = attributeValues[attributeIndex];
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getFilteredCollection()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
|
||
|
|
out.writeByte(0);
|
||
|
|
out.writeShort((short) getFilteredCount());
|
||
|
|
out.writeObject(type);
|
||
|
|
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if((attributeMask == null) || ((attributeNumbers[index] < attributeMask.length) && (attributeMask[attributeNumbers[index]]))) {
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumbers[index]);
|
||
|
|
out.writeUTF(attributeMetadata.getName());
|
||
|
|
out.writeObject(attributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//writeExternal()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(com.common.io.IObjectOutputStream out) throws java.io.IOException {
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
|
||
|
|
out.writeByte(0);
|
||
|
|
out.writeShort((short) getFilteredCount());
|
||
|
|
out.writeObject(type);
|
||
|
|
|
||
|
|
//TODO: Should use the more specific stream code to optimize the attribute names by sending indexes instead.//
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
if((attributeMask == null) || ((attributeNumbers[index] < attributeMask.length) && (attributeMask[attributeNumbers[index]]))) {
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumbers[index]);
|
||
|
|
out.writeUTF(attributeMetadata.getName());
|
||
|
|
out.writeObject(attributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//writeExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
in.readByte(); //Version//
|
||
|
|
TypeMetadata typeMetadata = null;
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
int index = 0;
|
||
|
|
|
||
|
|
attributeCount = in.readShort() & 0xFFFF;
|
||
|
|
type = (Class) in.readObject();
|
||
|
|
attributeNumbers = new int[attributeCount];
|
||
|
|
attributeValues = new Object[attributeCount];
|
||
|
|
typeMetadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||
|
|
|
||
|
|
for(int counter = 0; counter < attributeCount; counter++) {
|
||
|
|
String attributeName = in.readUTF();
|
||
|
|
Object value = in.readObject();
|
||
|
|
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
attributeNumbers[index] = attributeMetadata.getNumber();
|
||
|
|
attributeValues[index] = value;
|
||
|
|
index++;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//The attribute does not exist in this version of the class.//
|
||
|
|
attributeCount--;
|
||
|
|
Debug.halt();
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(com.common.io.IObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
in.readByte(); //Version//
|
||
|
|
TypeMetadata typeMetadata = null;
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
int index = 0;
|
||
|
|
|
||
|
|
attributeCount = in.readShort() & 0xFFFF;
|
||
|
|
type = (Class) in.readObject();
|
||
|
|
attributeNumbers = new int[attributeCount];
|
||
|
|
attributeValues = new Object[attributeCount];
|
||
|
|
typeMetadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||
|
|
|
||
|
|
//TODO: Should use the more specific stream code to optimize the attribute names by sending indexes instead.//
|
||
|
|
for(int counter = 0; counter < attributeCount; counter++) {
|
||
|
|
String attributeName = in.readUTF();
|
||
|
|
Object value = in.readObject();
|
||
|
|
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
attributeNumbers[index] = attributeMetadata.getNumber();
|
||
|
|
attributeValues[index] = value;
|
||
|
|
index++;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//The attribute does not exist in this version of the class.//
|
||
|
|
attributeCount--;
|
||
|
|
Debug.halt();
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
}//readExternal()//
|
||
|
|
}//AttributeCollection//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2003,2006<p>
|
||
|
|
* Represents a null attribute value. An actual null attribute value indicates that the attribute has not yet been assigned a value.
|
||
|
|
*/
|
||
|
|
private static final class NullValue implements java.io.Externalizable, com.common.io.IExternalizable {
|
||
|
|
private NullValue() {
|
||
|
|
}//NullValue()//
|
||
|
|
public boolean equals(Object object) {
|
||
|
|
return object instanceof NullValue;
|
||
|
|
}//equals()//
|
||
|
|
public int hashCode() {
|
||
|
|
return -1;
|
||
|
|
}//hashCode()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(com.common.io.IObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void writeExternal(com.common.io.IObjectOutputStream out) throws java.io.IOException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
|
||
|
|
}//readExternal()//
|
||
|
|
}//NullValue//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2003,2006<p>
|
||
|
|
* Represents an attribute value that has not yet been loaded, but must be recalculated when it is loaded.
|
||
|
|
*/
|
||
|
|
private static final class RecalculateValue implements java.io.Externalizable, com.common.io.IExternalizable {
|
||
|
|
/**
|
||
|
|
* RecalculateValue constructor.
|
||
|
|
*/
|
||
|
|
private RecalculateValue() {
|
||
|
|
}//RecalculateValue()//
|
||
|
|
public boolean equals(Object object) {
|
||
|
|
return object instanceof RecalculateValue;
|
||
|
|
}//equals()//
|
||
|
|
public int hashCode() {
|
||
|
|
return -1;
|
||
|
|
}//hashCode()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(com.common.io.IObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void writeExternal(com.common.io.IObjectOutputStream out) throws java.io.IOException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
|
||
|
|
}//readExternal()//
|
||
|
|
}//RecalculateValue//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2006<p>
|
||
|
|
* Encapsulates a listener for a calculated attribute.
|
||
|
|
* The listening can be suspended for a perticular thread durring an operation that may generate change events.
|
||
|
|
* The change events can then be grouped into one update of the attribute's value, or can be ignored.
|
||
|
|
* Only one thread at a time should update the attribute's value and that thread should ignore any events generated.
|
||
|
|
*/
|
||
|
|
private class CalculatedAttributeListener implements IEntity.ICalculatedAttributeListener {
|
||
|
|
/** The attribute that the listener is working for. */
|
||
|
|
private int attribute = 0;
|
||
|
|
|
||
|
|
protected CalculatedAttributeListener(int attribute) {
|
||
|
|
this.attribute = attribute;
|
||
|
|
}//CalculatedAttributeListener()//
|
||
|
|
public Class getReferenceType() {
|
||
|
|
return null;
|
||
|
|
}//getReferenceType()//
|
||
|
|
public void run(Object object, int type, int flags, Object oldValue, Object newValue) {
|
||
|
|
//Ignore changes that occur during initialization or releasing of the listener and changes due to the attribute being initialized.//
|
||
|
|
if((!AttributeBinding.isListenerInitializing(flags)) && (!AttributeBinding.isListenerReleasing(flags)) && (!AttributeBinding.isAttributeUninitialized(flags)) && (type != TYPE_INITIALIZED)) {
|
||
|
|
SuspendCalculatedAttributesData suspendData = (SuspendCalculatedAttributesData) calculatedAttributeSuspendData.get();
|
||
|
|
|
||
|
|
if(suspendData == null) {
|
||
|
|
updateCalculatedAttribute();
|
||
|
|
}//if//
|
||
|
|
else if(suspendData.getCalculatedAttributeListenerSet() != null) {
|
||
|
|
suspendData.getCalculatedAttributeListenerSet().add(this);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//run()//
|
||
|
|
public void run(Object object, int flags, IList addedValues, IList removedValues) {
|
||
|
|
if((!CollectionBinding.isListenerInitializing(flags)) && (!CollectionBinding.isListenerReleasing(flags))) {
|
||
|
|
SuspendCalculatedAttributesData suspendData = (SuspendCalculatedAttributesData) calculatedAttributeSuspendData.get();
|
||
|
|
|
||
|
|
if(suspendData == null) {
|
||
|
|
updateCalculatedAttribute();
|
||
|
|
}//if//
|
||
|
|
else if(suspendData.getCalculatedAttributeListenerSet() != null) {
|
||
|
|
suspendData.getCalculatedAttributeListenerSet().add(this);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//run()//
|
||
|
|
public IEntity getEntity() {
|
||
|
|
return (IEntity) getSupportedObject();
|
||
|
|
}//getEntity()//
|
||
|
|
public TypeCallbackHandler getTypeCallbackHandler() {
|
||
|
|
return typeMetadata.getTypeCallbackHandler();
|
||
|
|
}//getTypeCallbackHandler()//
|
||
|
|
public AttributeSupport getAttributeSupport() {
|
||
|
|
return AttributeSupport.this;
|
||
|
|
}//getAttributeSupport()//
|
||
|
|
public void updateCalculatedAttribute() {
|
||
|
|
Object value;
|
||
|
|
|
||
|
|
//Single threaded context data can be processed without locking. Locking in that context could cause lock errors due to lazy loading or reflection loading needing an additional lock.//
|
||
|
|
if(getReflectionContext() != null) {
|
||
|
|
value = typeMetadata.getTypeCallbackHandler().calculatedAttributeUpdate((IEntity) getSupportedObject(), typeMetadata.getAttributeMetadata(attribute).getIdentifier());
|
||
|
|
|
||
|
|
//Short circuit any changes that are 'non-changes'. Logical equality can always be used here for this purpose since calculated attributes should always be immutable.//
|
||
|
|
if(!Comparator.equals(getAttributeValue(attribute), value)) {
|
||
|
|
setAttributeValue(attribute, value, CONTEXT_UNTRUSTED);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
Monitor monitor = getSupportedObject().getMonitor();
|
||
|
|
|
||
|
|
//TODO: This would be nice (not allowing other locks), but it causes problems when the update causes data to be loaded which then initializes new model listeners which require locks.
|
||
|
|
//We could avoid this by using LoadAttributeContext to force data to be loaded prior to updating the calculated attribute, but that is probably unnecessary.
|
||
|
|
|
||
|
|
//Monitor.lock(monitor, 0, false, "Calculated attributes may not access non-part-of objects. This prevents data corruption in the repository and in the model.");
|
||
|
|
Monitor.lock(monitor);
|
||
|
|
|
||
|
|
//If the calculated attribute has not yet been accessed then don't recalculate - instead flag it as needing recalculation.//
|
||
|
|
if(currentAttributeValues[attribute] == null) {
|
||
|
|
currentAttributeValues[attribute] = RECALCULATE_VALUE;
|
||
|
|
}//if//
|
||
|
|
else if(!(currentAttributeValues[attribute] instanceof RecalculateValue)) {
|
||
|
|
value = typeMetadata.getTypeCallbackHandler().calculatedAttributeUpdate((IEntity) getSupportedObject(), typeMetadata.getAttributeMetadata(attribute).getIdentifier());
|
||
|
|
|
||
|
|
//Short circuit any changes that are 'non-changes'. Logical equality can always be used here for this purpose since calculated attributes should always be immutable.//
|
||
|
|
//Note: Changed to internalGetAttributeValue from getAttributeValue because calling the latter caused the value to be recalculated and set.//
|
||
|
|
if(!Comparator.equals(internalGetAttributeValue(attribute), value)) {
|
||
|
|
setAttributeValue(attribute, value, CONTEXT_UNTRUSTED);
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
unlock(getSupportedObject());
|
||
|
|
}//else//
|
||
|
|
}//updateCalculatedAttribute()//
|
||
|
|
}//CalculatedAttributeListener//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Copyright Declarative Engineering LLC 2005,2006<p>
|
||
|
|
* Encapsulates the data for a suspend of the calculated attribute processing.
|
||
|
|
*/
|
||
|
|
private static class SuspendCalculatedAttributesData {
|
||
|
|
private LiteHashSet calculatedAttributeListenerSet = null;
|
||
|
|
private int suspendCount = 1;
|
||
|
|
/**
|
||
|
|
* SuspendCalculatedAttributesData constructor.
|
||
|
|
* @param updateOnResume Whether the resume should update the calculated attributes.
|
||
|
|
*/
|
||
|
|
public SuspendCalculatedAttributesData(boolean updateOnResume) {
|
||
|
|
this.calculatedAttributeListenerSet = updateOnResume ? new LiteHashSet(10) : null;
|
||
|
|
}//SuspendCalculatedAttributesData()//
|
||
|
|
/**
|
||
|
|
* Gets the suspend count. This defaults to 1 for a new data instance.
|
||
|
|
* @return The number of times the calculated attribute processing has been suspended with compatible parameters.
|
||
|
|
*/
|
||
|
|
public int getSuspendCount() {
|
||
|
|
return suspendCount;
|
||
|
|
}//getSuspendCount()//
|
||
|
|
/**
|
||
|
|
* Sets the suspend count.
|
||
|
|
* @param suspendCount The number of times the calculated attribute processing has been suspended with compatible parameters.
|
||
|
|
*/
|
||
|
|
public void setSuspendCount(int suspendCount) {
|
||
|
|
this.suspendCount = suspendCount;
|
||
|
|
}//setSuspendCount()//
|
||
|
|
/**
|
||
|
|
* Gets the set of CalculatedAttributeListener instances that require updating.
|
||
|
|
* @return The set of calculated attribute listeners needing to be updated.
|
||
|
|
*/
|
||
|
|
public LiteHashSet getCalculatedAttributeListenerSet() {
|
||
|
|
return calculatedAttributeListenerSet;
|
||
|
|
}//getCalculatedAttributeListenerSet()//
|
||
|
|
}//SuspendCalculatedAttributesData//
|
||
|
|
/**
|
||
|
|
* AttributeSupport constructor.
|
||
|
|
* @param supportedObject The object being supported.
|
||
|
|
*/
|
||
|
|
private AttributeSupport(IEntity supportedObject) {
|
||
|
|
super(supportedObject);
|
||
|
|
this.supportedObject = supportedObject;
|
||
|
|
}//AttributeSupport()//
|
||
|
|
/**
|
||
|
|
* AttributeSupport constructor.
|
||
|
|
* @param supportedObject The object being supported.
|
||
|
|
* @param eventSupport The supported object's event support object. If available, this is used to fire attribute change events.
|
||
|
|
*/
|
||
|
|
public AttributeSupport(IEntity supportedObject, EventSupport eventSupport) {
|
||
|
|
super(supportedObject);
|
||
|
|
|
||
|
|
initialize(supportedObject, eventSupport);
|
||
|
|
}//AttributeSupport()//
|
||
|
|
/**
|
||
|
|
* Gets the access object for the attribute support.
|
||
|
|
* <p>Warning: This object can only be retrieved once and should be retrieved immediatly after creating the attribute support.</p>
|
||
|
|
* @return The access object which provides the user of this attribute support with access to normally hidden methods to perform such actions as getting and setting attribute values.
|
||
|
|
*/
|
||
|
|
public AttributeSupportAccess getAttributeSupportAccess() {
|
||
|
|
AttributeSupportAccess result = access;
|
||
|
|
|
||
|
|
access = null;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getAttributeSupportAccess()//
|
||
|
|
/**
|
||
|
|
* Applies an attribute collection to this set of attributes. Values in the collection will replace values in this attribute set.
|
||
|
|
* @param attributeCollection A collection of attribute value pairs created by another attribute support (presumably associated with an object of the same type as is associated with this attribute support).
|
||
|
|
* @param context The change context for the attribute changes. This should be UNTRUSTED unless this is a synchronize result (after synchronizing this object, the attributes update to reflect what was accepted by the reflected object). This may use other flags to tell the attribute system why the attribute is changing.
|
||
|
|
* @param updateReflections Whether the reflections should be updated. If this is false then the caller is responsible for that action.
|
||
|
|
*/
|
||
|
|
void applyAttributeCollection(AttributeCollection attributeCollection, int context, boolean updateReflections) {
|
||
|
|
int[] numbers = attributeCollection.attributeNumbers;
|
||
|
|
Object[] values = attributeCollection.attributeValues;
|
||
|
|
|
||
|
|
//Apply each attribute update.//
|
||
|
|
for(int index = 0; index < attributeCollection.attributeCount; index++) {
|
||
|
|
Object value = values[index];
|
||
|
|
|
||
|
|
//If this is a reflection and the value is not a reflection in this reflection's context, then create a reflection from the value.//
|
||
|
|
if((value instanceof IReflectable) && (isReflection())) {
|
||
|
|
value = getReflectionContext().createReflection((IReflectable) value);
|
||
|
|
//Set the attribute value. If we are synchronizing then this is an untrusted change, otherwise this is a reflection being updated by the reflected object and it is trusted.//
|
||
|
|
setAttributeValue(numbers[index], value, context, updateReflections);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//Set the attribute value. If we are synchronizing then this is an untrusted change, otherwise this is a reflection being updated by the reflected object and it is trusted.//
|
||
|
|
setAttributeValue(numbers[index], value, context, updateReflections);
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
}//applyAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Clones this attribute support object.
|
||
|
|
* @param entity The entity clone that this attribute support will be supporting.
|
||
|
|
* @param eventSupport The entity's event support through which events are fired when attributes change.
|
||
|
|
* @return The cloned attribute support instance linked to the provided supported object.
|
||
|
|
*/
|
||
|
|
public Object clone(IEntity supportedObject, EventSupport eventSupport) {
|
||
|
|
AttributeSupport clone = new AttributeSupport(supportedObject);
|
||
|
|
|
||
|
|
//TODO: Make this call the clone method on the super classes.
|
||
|
|
clone.currentAttributeValues = (Object[]) currentAttributeValues.clone();
|
||
|
|
clone.alteredAttributeValues = (Object[]) alteredAttributeValues.clone();
|
||
|
|
clone.localChangeListeners = new LocalAttributeChangeListener[currentAttributeValues.length];
|
||
|
|
clone.calculatedAttributeListeners = new ModelListener[typeMetadata.getCalculatedAttributeCount()];
|
||
|
|
clone.eventSupport = eventSupport;
|
||
|
|
clone.typeMetadata = typeMetadata;
|
||
|
|
clone.setSupportedObjectAttributeSupport(clone);
|
||
|
|
clone.canUpdate = canUpdate;
|
||
|
|
clone.hasChanged = hasChanged;
|
||
|
|
clone.virtualObjectChangeCounter = virtualObjectChangeCounter;
|
||
|
|
//Clear the references to any repository.//
|
||
|
|
clone.repositoryIdentifier = repositoryIdentifier;
|
||
|
|
clone.typeRepositoryMetadata = typeRepositoryMetadata;
|
||
|
|
|
||
|
|
return clone;
|
||
|
|
}//clone()//
|
||
|
|
/**
|
||
|
|
* Prepares this attribute support for cloning by lazy loading any required attributes and calling preClone on all part-of attributes.
|
||
|
|
* @param metadata The metadata for the clone job.
|
||
|
|
* @param cloneContext The context for the clone operation.
|
||
|
|
*/
|
||
|
|
public void preClone(MetadataContainer metadata, CloneContext cloneContext) {
|
||
|
|
EntityMetadata entityMetadata = (EntityMetadata) metadata.getMetadata(getSupportedObject().getClass());
|
||
|
|
IntHashSet includedAttributes = entityMetadata != null ? entityMetadata.getAttributes() : IntHashSet.EMPTY_INT_HASH_SET;
|
||
|
|
|
||
|
|
suspendWeakReferencesCount++;
|
||
|
|
|
||
|
|
//Lazy load the included attributes.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(index);
|
||
|
|
|
||
|
|
//De-reference the values as necessary.//
|
||
|
|
if(currentAttributeValues[index] instanceof Reference) {
|
||
|
|
currentAttributeValues[index] = ((Reference) currentAttributeValues[index]).get();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(currentAttributeValues[index] == null) {
|
||
|
|
if(includedAttributes != null) {
|
||
|
|
if((attributeMetadata.isLazy()) && (includedAttributes.containsValue(index))) {
|
||
|
|
lazyLoadAttribute(index, attributeMetadata);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
if((attributeMetadata.isLazy()) && (attributeMetadata.isPartOf())) {
|
||
|
|
lazyLoadAttribute(index, attributeMetadata);
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Call pre-clone on all included part-of references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = alteredAttributeValues[index] == null ? currentAttributeValues[index] : alteredAttributeValues[index];
|
||
|
|
|
||
|
|
if((value instanceof ISupportsContainment) && (getIsAttributePartOf(index))) {
|
||
|
|
//Exclude reflections if the metadata is flagged to do so.//
|
||
|
|
if(metadata == null || (!metadata.excludeReflections() || !(value instanceof IReflectable) || !((IReflectable) value).isReflection())) {
|
||
|
|
if((includedAttributes == null) || (includedAttributes.containsValue(index))) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
//If we haven't already cloned this object then do so now.//
|
||
|
|
handler.preClone(value, metadata, cloneContext);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//preClone()//
|
||
|
|
/**
|
||
|
|
* Replaces the clone's attributes that are marked as part-of or collections with clones of the values so that the resulting clone is a meta-object copy.
|
||
|
|
* Also clears transient and key attributes, and clears any repository bindings.
|
||
|
|
* @param context The cloning context used to track which objects have been cloned in the meta-object cloning process.
|
||
|
|
* @param metadata The metadata for the clone job.
|
||
|
|
* @param clonedSupport The cloned attribute support.
|
||
|
|
*/
|
||
|
|
public void completeClone(CloneContext context, MetadataContainer metadata, AttributeSupport clonedSupport) {
|
||
|
|
EntityMetadata entityMetadata = (EntityMetadata) metadata.getMetadata(getSupportedObject().getClass());
|
||
|
|
IntHashSet includedAttributes = entityMetadata != null ? entityMetadata.getAttributes() : null;
|
||
|
|
|
||
|
|
//Note: We should be locked on the cloned entity's monitor.//
|
||
|
|
clonedSupport.suspendWeakReferencesCount--;
|
||
|
|
|
||
|
|
//Clone part-of entity and collection references, clear key references for the current database, copy all other references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = alteredAttributeValues[index] == null ? currentAttributeValues[index] : alteredAttributeValues[index];
|
||
|
|
|
||
|
|
if(value != null) {
|
||
|
|
boolean isPartOf = getIsAttributePartOf(index);
|
||
|
|
|
||
|
|
if(isPartOf && ((includedAttributes == null) || (includedAttributes.containsValue(index)))) {
|
||
|
|
if(value instanceof ISupportsContainment) {
|
||
|
|
//Exclude reflections if the metadata is flagged to do so. If excluding reflections and this attribute references a reflection then leave the reflection reference alone.//
|
||
|
|
if(metadata == null || (!metadata.excludeReflections() || !(value instanceof IReflectable) || !((IReflectable) value).isReflection())) {
|
||
|
|
Object valueClone = context.getClone(value);
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
//If we haven't already cloned this object then do so now.//
|
||
|
|
if(valueClone == null) {
|
||
|
|
valueClone = handler.clone(value, metadata, context);
|
||
|
|
context.setClone(value, valueClone);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
currentAttributeValues[index] = valueClone;
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
//Set the clone's monitor because the object is part-of.//
|
||
|
|
handler.setMonitor(valueClone, supportedObject, isPartOf);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
currentAttributeValues[index] = NULL_VALUE;
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else if(value instanceof RecalculateValue) {
|
||
|
|
//Do nothing. The recalculate value object must be copied.//
|
||
|
|
}//else if//
|
||
|
|
else if(includedAttributes != null) {
|
||
|
|
if(!includedAttributes.containsValue(index)) {
|
||
|
|
currentAttributeValues[index] = null;
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
}//if//
|
||
|
|
else if(!(value instanceof NullValue)) {
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
|
||
|
|
if(getIsCollection(index) && (value instanceof ISupportsContainment)) {
|
||
|
|
Object valueClone = context.getClone(value);
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
//If we haven't already cloned this collection then do so now.//
|
||
|
|
if(valueClone == null) {
|
||
|
|
valueClone = handler.clone(value, metadata, context);
|
||
|
|
context.setClone(value, valueClone);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
currentAttributeValues[index] = valueClone;
|
||
|
|
//Set the clone's monitor because the object is partially part-of (Collections may be partially part-of because the collection is always part of the entity if declared as a collection, but the collected values may not be.).//
|
||
|
|
handler.setMonitor(valueClone, supportedObject, false);
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//else if//
|
||
|
|
else if(getIsAttributeTransient(index)) {
|
||
|
|
//Note: We are assuming that transient is equivalent to non-cloneable. This would include attribute values such as AggregateList which doesn't copy well in any situation and should always be lazy loaded.//
|
||
|
|
currentAttributeValues[index] = null;
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
}//else if//
|
||
|
|
//Commented this out since it appears to break the stated functionality of the clone if no metadata is provided. If no metadata is provided then the attributes should all be cloned.//
|
||
|
|
// else if(!(value instanceof NullValue)) {
|
||
|
|
// currentAttributeValues[index] = NULL_VALUE;
|
||
|
|
// alteredAttributeValues[index] = null;
|
||
|
|
// }//else if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Re-reference the data in the cloned object as necessary.//
|
||
|
|
if((clonedSupport.allowWeakReferences()) && (!clonedSupport.isReflection()) && (clonedSupport.currentAttributeValues[index] != null) && !(clonedSupport.currentAttributeValues[index] instanceof NullValue) && !(clonedSupport.currentAttributeValues[index] instanceof Reference) && (clonedSupport.getIsAttributeWeak(index))) {
|
||
|
|
clonedSupport.currentAttributeValues[index] = new SoftReference(clonedSupport.currentAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
recalculateChangeFlags();
|
||
|
|
//Clear the references to any repository.//
|
||
|
|
repositoryIdentifier = null;
|
||
|
|
typeRepositoryMetadata = null;
|
||
|
|
//Add this object as a listener for the post clone cleanup.//
|
||
|
|
context.addPostCloneCleanupListener(this);
|
||
|
|
}//completeClone()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.attribute.CloneContext.IPostCloneCleanupListener#postCloneCleanup(com.foundation.attribute.CloneContext)
|
||
|
|
*/
|
||
|
|
public void postCloneCleanup(CloneContext context) {
|
||
|
|
//Replace any non-cloned values with their clones if they were cloned by another part of the cloning process.//
|
||
|
|
//For example, this would catch back references, or references to objects that are also in a part-of collection in the same object, etc.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = alteredAttributeValues[index] == null ? currentAttributeValues[index] : alteredAttributeValues[index];
|
||
|
|
|
||
|
|
value = context.getClone(value);
|
||
|
|
|
||
|
|
if(value != null) {
|
||
|
|
currentAttributeValues[index] = value;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//postCloneCleanup()//
|
||
|
|
/**
|
||
|
|
* Loads attributes within the given context. The context controls which attributes are loaded from which objects.
|
||
|
|
* @param context The context for the load operation.
|
||
|
|
*/
|
||
|
|
public void loadAttributes(LoadAttributesContext context) {
|
||
|
|
MetadataContainer metadataContainer = context.getMetadataContainer();
|
||
|
|
int options = context.getOptions();
|
||
|
|
boolean traverseLogicalObject = (options & LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT) > 0;
|
||
|
|
EntityMetadata entityMetadata = metadataContainer != null ? (EntityMetadata) metadataContainer.getMetadata(getSupportedObject().getClass()) : null;
|
||
|
|
|
||
|
|
suspendWeakReferencesCount++;
|
||
|
|
|
||
|
|
//Lazy load the included attributes and traverse those that are part-of.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(index);
|
||
|
|
|
||
|
|
//De-reference the values as necessary.//
|
||
|
|
if(attributeMetadata.isWeak() && (currentAttributeValues[index] instanceof Reference)) {
|
||
|
|
currentAttributeValues[index] = ((Reference) currentAttributeValues[index]).get();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(context.isIncluded(attributeMetadata, entityMetadata)) {
|
||
|
|
//If this is a reflection and the attribute is reflectable then it must be retrieved from the reflection.//
|
||
|
|
if(isReflection() && attributeMetadata.isReflectable()) {
|
||
|
|
//If the attribute value is not yet set then try to load it from the reflected object.//
|
||
|
|
if((currentAttributeValues[index] == null) || (currentAttributeValues[index] instanceof RecalculateValue)) {
|
||
|
|
loadReflectedAttribute(index, attributeMetadata);
|
||
|
|
}//if//
|
||
|
|
else if(currentAttributeValues[index] == VALUE_LOADING) {
|
||
|
|
//Wait for the attribute to be loaded.//
|
||
|
|
getAttributeValue(index, true, attributeMetadata, null);
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
//If the attribute value is includable in the loading of attributes then register a listener and try to include the value.//
|
||
|
|
if(attributeMetadata.isPartOf() && traverseLogicalObject) {
|
||
|
|
addAttributeChangeListener(index, context);
|
||
|
|
|
||
|
|
if(currentAttributeValues[index] instanceof ISupportsContainment) {
|
||
|
|
context.include(currentAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//If the attribute is lazy then ensure it gets loaded.//
|
||
|
|
if(attributeMetadata.isLazy()) {
|
||
|
|
//Load the attribute from the reflected object.//
|
||
|
|
if((currentAttributeValues[index] == null) || (currentAttributeValues[index] instanceof RecalculateValue)) {
|
||
|
|
if(isReflection()) {
|
||
|
|
loadReflectedAttribute(index, attributeMetadata);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
lazyLoadAttribute(index, attributeMetadata);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else if(currentAttributeValues[index] == VALUE_LOADING) {
|
||
|
|
//Wait for the attribute to be loaded.//
|
||
|
|
getAttributeValue(index, true, attributeMetadata, null);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If the attribute value is includable in the loading of attributes then register a listener and try to include the value.//
|
||
|
|
if(attributeMetadata.isPartOf() && traverseLogicalObject) {
|
||
|
|
//Register a change listener so we know if there have been changes. Note: Any value could be a reference to something includable.//
|
||
|
|
addAttributeChangeListener(index, context);
|
||
|
|
|
||
|
|
//If the value supports containment then include it in the loading.//
|
||
|
|
if(currentAttributeValues[index] instanceof ISupportsContainment) {
|
||
|
|
context.include(currentAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//loadAttributes()//
|
||
|
|
/**
|
||
|
|
* Called after the loadAttributes method and after the attributes have been used as needed.
|
||
|
|
* This method does not need to call postLoadAttributes on referenced objects since the context will call it on all objects it called loadAttributes on.
|
||
|
|
* It does need to re-apply weakness where necessary and remove listeners.
|
||
|
|
* @param context The context for the load operation.
|
||
|
|
*/
|
||
|
|
public void postLoadAttributes(LoadAttributesContext context) {
|
||
|
|
EntityMetadata entityMetadata = context.getMetadataContainer() != null ? (EntityMetadata) context.getMetadataContainer().getMetadata(getSupportedObject().getClass()) : null;
|
||
|
|
|
||
|
|
suspendWeakReferencesCount--;
|
||
|
|
|
||
|
|
if(suspendWeakReferencesCount == 0) {
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
AttributeMetadata metadata = typeMetadata.getAttributeMetadata(index);
|
||
|
|
|
||
|
|
if(allowWeakReferences() && metadata.isWeak() && getIsAttributeRealized(index) && !(currentAttributeValues[index] instanceof NullValue)) {
|
||
|
|
currentAttributeValues[index] = new SoftReference(currentAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(entityMetadata != null) {
|
||
|
|
if(entityMetadata.getAttributes().containsValue(index)) {
|
||
|
|
removeAttributeChangeListener(index, context);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else if(metadata.isPartOf() && ((context.getOptions() & LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT) > 0)) {
|
||
|
|
removeAttributeChangeListener(index, context);
|
||
|
|
}//else if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
AttributeMetadata metadata = typeMetadata.getAttributeMetadata(index);
|
||
|
|
|
||
|
|
if(metadata.isPartOf()) {
|
||
|
|
if(entityMetadata != null) {
|
||
|
|
if(entityMetadata.getAttributes().containsValue(index)) {
|
||
|
|
removeAttributeChangeListener(index, context);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else if((context.getOptions() & LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT) > 0) {
|
||
|
|
removeAttributeChangeListener(index, context);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//else//
|
||
|
|
}//postLoadAttributes()//
|
||
|
|
/**
|
||
|
|
* Creates an attribute collection useable to create a reflection of the object.
|
||
|
|
* This attribute collection will assume that unassigned attributes that don't have a lazy-load handler will be assigned a null value.
|
||
|
|
* The collection will not include attributes whose values have not yet been assigned and which have an associated lazy-load handler.
|
||
|
|
* Reflections can request attribute values for those they were not given at a later time.
|
||
|
|
* @param attributeCollection A collection of attribute data containing all attributes.
|
||
|
|
* @param isAttributeReflected An array of flags indicating for each declared attribute, whether it should be included in the attribute collection.
|
||
|
|
* @param isRemote Whether the reflection being created is remote. If it is remote then reflectable objects should not be serialized, but proxied instead.
|
||
|
|
*/
|
||
|
|
protected void createReflectionAttributeCollection(AttributeCollection attributeCollection, boolean[] isAttributeReflected, boolean isRemote) {
|
||
|
|
int attributeCount = getAttributeCount();
|
||
|
|
int reflectedAttributeCount = attributeCount;
|
||
|
|
int attributeIndex;
|
||
|
|
int[] numbers;
|
||
|
|
Object[] values;
|
||
|
|
|
||
|
|
for(int attributeNumber = 0; attributeNumber < isAttributeReflected.length; attributeNumber++) {
|
||
|
|
if(!isAttributeReflected[attributeNumber]) {
|
||
|
|
reflectedAttributeCount--;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
attributeIndex = reflectedAttributeCount;
|
||
|
|
numbers = new int[reflectedAttributeCount];
|
||
|
|
values = new Object[reflectedAttributeCount];
|
||
|
|
|
||
|
|
for(int attributeNumber = 0; attributeNumber < attributeCount; attributeNumber++) {
|
||
|
|
if(isAttributeReflected[attributeNumber]) {
|
||
|
|
Object value = getAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
attributeIndex--;
|
||
|
|
numbers[attributeIndex] = attributeNumber;
|
||
|
|
values[attributeIndex] = value;
|
||
|
|
|
||
|
|
if((isRemote) && (values[attributeIndex] instanceof IReflectable)) {
|
||
|
|
values[attributeIndex] = getReflectionProxy((IReflectable) values[attributeIndex]);
|
||
|
|
}//if//
|
||
|
|
else if((!isRemote) && !(values[attributeIndex] instanceof IReflectable)) {
|
||
|
|
values[attributeIndex] = cloneValue(values[attributeIndex]);
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
isAttributeReflected[attributeNumber] = true;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
attributeCollection.attributeNumbers = numbers;
|
||
|
|
attributeCollection.attributeValues = values;
|
||
|
|
attributeCollection.attributeCount = numbers.length;
|
||
|
|
attributeCollection.type = supportedObject.getClass();
|
||
|
|
}//createReflectionAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Creates an attribute collection useable to create a reflection of the object.
|
||
|
|
* This attribute collection will assume that unassigned attributes that don't have a lazy-load handler will be assigned a null value.
|
||
|
|
* The collection will not include attributes whose values have not yet been assigned and which have an associated lazy-load handler.
|
||
|
|
* Reflections can request attribute values for those they were not given at a later time.
|
||
|
|
* @param attributeCollection A collection of attribute data containing all attributes.
|
||
|
|
* @param isRemote Whether the reflection being created is remote. If it is remote then reflectable objects should not be serialized, but proxied instead.
|
||
|
|
* @param createReflectDataContext The context for the creation of the reflect data.
|
||
|
|
* @return An array of flags indicating for each declared attribute, whether it was included in the attribute collection.
|
||
|
|
*/
|
||
|
|
protected boolean[] createReflectionAttributeCollection(AttributeCollection attributeCollection, boolean isRemote, CreateReflectDataContext createReflectDataContext) {
|
||
|
|
int attributeCount = getAttributeCount();
|
||
|
|
int reflectedAttributeCount = attributeCount;
|
||
|
|
int attributeIndex;
|
||
|
|
int[] numbers;
|
||
|
|
Object[] values;
|
||
|
|
boolean[] isAttributeReflected = new boolean[attributeCount];
|
||
|
|
//TODO: Should we allow use of super type metadata if the exact type does not match?
|
||
|
|
EntityMetadata metadata = (EntityMetadata) (createReflectDataContext.getMetadataContainer() != null ? createReflectDataContext.getMetadataContainer().getMetadata(getSupportedObject().getClass()) : null);
|
||
|
|
|
||
|
|
if(metadata != null && !createReflectDataContext.isSynchronizationResult()) {
|
||
|
|
for(int attributeNumber = 0; attributeNumber < attributeCount; attributeNumber++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
IntHashSet attributes = metadata.getAttributes();
|
||
|
|
|
||
|
|
if((!attributes.containsValue(attributeNumber)) || (!attributeMetadata.isReflectable())) {
|
||
|
|
isAttributeReflected[attributeNumber] = false;
|
||
|
|
reflectedAttributeCount--;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
isAttributeReflected[attributeNumber] = true;
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
for(int attributeNumber = 0; attributeNumber < attributeCount; attributeNumber++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
Object value = getAttributeValue(attributeNumber, false, attributeMetadata, NOT_LAZY_LOADED);
|
||
|
|
boolean isNotLazyLoaded = value == NOT_LAZY_LOADED;
|
||
|
|
|
||
|
|
if(createReflectDataContext.isSynchronizationResult()) {
|
||
|
|
//Reflect almost everything if this is the result of a synchronization. This will solve problems such as attributes whose values are created by the repository and are displayed by a view already. In that case the value may be not-set here, and will get set in a moment, but without reflecting everything will never be sent to the reflection.//
|
||
|
|
if(!attributeMetadata.isReflectable()) {
|
||
|
|
isAttributeReflected[attributeNumber] = false;
|
||
|
|
reflectedAttributeCount--;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
isAttributeReflected[attributeNumber] = true;
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
if((isNotLazyLoaded) || (value instanceof IReflectable) || (!attributeMetadata.isReflectable())) {
|
||
|
|
isAttributeReflected[attributeNumber] = false;
|
||
|
|
reflectedAttributeCount--;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
isAttributeReflected[attributeNumber] = true;
|
||
|
|
}//else//
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
attributeIndex = reflectedAttributeCount;
|
||
|
|
numbers = new int[reflectedAttributeCount];
|
||
|
|
values = new Object[reflectedAttributeCount];
|
||
|
|
|
||
|
|
//Iterate over the attributes and collect those that are reflected.//
|
||
|
|
for(int attributeNumber = 0; attributeNumber < attributeCount; attributeNumber++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
Object value = getAttributeValue(attributeNumber, false, attributeMetadata, null);
|
||
|
|
|
||
|
|
if(isAttributeReflected[attributeNumber]) {
|
||
|
|
attributeIndex--;
|
||
|
|
numbers[attributeIndex] = attributeNumber;
|
||
|
|
values[attributeIndex] = value instanceof NullValue || !attributeMetadata.isReflectable() ? null : value;
|
||
|
|
|
||
|
|
//Create reflection data for part-of objects.//
|
||
|
|
if(values[attributeIndex] instanceof IReflectable) {
|
||
|
|
//Proxy the reflectable value if the reflection will be remote.//
|
||
|
|
if(isRemote) {
|
||
|
|
values[attributeIndex] = getReflectionProxy((IReflectable) values[attributeIndex]);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
createReflectDataContext.include((IReflectable) values[attributeIndex]);
|
||
|
|
}//if//
|
||
|
|
//Clone if the reflection is not remote AND the value is not for sure immutable.//
|
||
|
|
else if(!isRemote && !(values[attributeIndex] == null || values[attributeIndex] instanceof String || values[attributeIndex] instanceof Number)) {
|
||
|
|
values[attributeIndex] = cloneValue(values[attributeIndex]);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
attributeCollection.attributeNumbers = numbers;
|
||
|
|
attributeCollection.attributeValues = values;
|
||
|
|
attributeCollection.attributeCount = numbers.length;
|
||
|
|
attributeCollection.type = supportedObject.getClass();
|
||
|
|
|
||
|
|
return isAttributeReflected;
|
||
|
|
}//createReflectionAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute collection of all attributes.
|
||
|
|
* <p>Note: This is not the most efficient way of creating save points because it will also collect the attribute names.</p>
|
||
|
|
* @param attributeNumbers The collection of attribute numbers that should be collected.
|
||
|
|
* @param proxyInterface An optional interface reference if the values implementing the interface should be proxied automatically.
|
||
|
|
* @return A collection of attribute data containing all attributes.
|
||
|
|
*/
|
||
|
|
AttributeCollection getAttributeCollection(int[] attributeNumbers, Class proxyInterface) {
|
||
|
|
int attributeCount = attributeNumbers.length;
|
||
|
|
int[] numbers = new int[attributeCount];
|
||
|
|
Object[] values = new Object[attributeCount];
|
||
|
|
AttributeCollection result = null;
|
||
|
|
|
||
|
|
//TODO: In order to avoid loading attributes that have not yet been initialized, we should call getAttributeValue(attributeNumber, false), and instead mark unloaded attributes so reflections may request them later.//
|
||
|
|
for(int index = 0; index < attributeCount; index++) {
|
||
|
|
numbers[index] = attributeNumbers[index];
|
||
|
|
values[index] = getAttributeValue(attributeNumbers[index]);
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
result = new AttributeCollection(numbers, values, supportedObject.getClass());
|
||
|
|
|
||
|
|
if(proxyInterface != null) {
|
||
|
|
result.proxyValues(proxyInterface);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute collection of all attributes.
|
||
|
|
* <p>Note: This is not the most efficient way of creating save points because it will also collect the attribute names.</p>
|
||
|
|
* @param attributeNumber The attribute number that should be collected.
|
||
|
|
* @param proxyInterface An optional interface reference if the values implementing the interface should be proxied automatically.
|
||
|
|
* @return A collection of attribute data containing all attributes.
|
||
|
|
*/
|
||
|
|
AttributeCollection getAttributeCollection(int attributeNumber, Class proxyInterface) {
|
||
|
|
int[] numbers = new int[1];
|
||
|
|
Object[] values = new Object[1];
|
||
|
|
AttributeCollection result = null;
|
||
|
|
|
||
|
|
//TODO: In order to avoid loading attributes that have not yet been initialized, we should call getAttributeValue(attributeNumber, false), and instead mark unloaded attributes so reflections may request them later.//
|
||
|
|
numbers[0] = attributeNumber;
|
||
|
|
values[0] = getAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
result = new AttributeCollection(numbers, values, supportedObject.getClass());
|
||
|
|
|
||
|
|
if(proxyInterface != null) {
|
||
|
|
result.proxyValues(proxyInterface);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute collection of all attributes.
|
||
|
|
* <p>Note: This is not the most efficient way of creating save points because it will also collect the attribute names.</p>
|
||
|
|
* @param proxyInterface An optional interface reference if the values implementing the interface should be proxied automatically.
|
||
|
|
* @return A collection of attribute data containing all attributes.
|
||
|
|
*/
|
||
|
|
AttributeCollection getAttributeCollection(Class proxyInterface) {
|
||
|
|
int attributeCount = getAttributeCount();
|
||
|
|
int[] numbers = new int[attributeCount];
|
||
|
|
Object[] values = new Object[attributeCount];
|
||
|
|
AttributeCollection result = null;
|
||
|
|
|
||
|
|
//TODO: In order to avoid loading attributes that have not yet been initialized, we should call getAttributeValue(attributeNumber, false), and instead mark unloaded attributes so reflections may request them later.//
|
||
|
|
for(int attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||
|
|
numbers[attributeIndex] = attributeIndex;
|
||
|
|
values[attributeIndex] = getAttributeValue(attributeIndex);
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
result = new AttributeCollection(numbers, values, supportedObject.getClass());
|
||
|
|
|
||
|
|
if(proxyInterface != null) {
|
||
|
|
result.proxyValues(proxyInterface);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Gets the number of attributes available for this object.
|
||
|
|
* @return The number of available attributes.
|
||
|
|
*/
|
||
|
|
public int getAttributeCount() {
|
||
|
|
return currentAttributeValues.length;
|
||
|
|
}//getAttributeCount()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute name for the given attribute number.
|
||
|
|
* @param attributeNumber The attribute's unique number.
|
||
|
|
* @return The attribute name, or <code>null</code> if the attribute has not been defined or given a name.
|
||
|
|
*/
|
||
|
|
public String getAttributeName(Attribute attribute) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attribute.getNumber()).getName();
|
||
|
|
}//getAttributeName()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute name for the given attribute number.
|
||
|
|
* @param attributeNumber The attribute's unique number.
|
||
|
|
* @return The attribute name, or <code>null</code> if the attribute has not been defined or given a name.
|
||
|
|
*/
|
||
|
|
public String getAttributeName(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).getName();
|
||
|
|
}//getAttributeName()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute identifier for the given attribute number.
|
||
|
|
* @param attributeNumber The attribute's unique number.
|
||
|
|
* @return The identifier originally returned when the attribute was initialized.
|
||
|
|
*/
|
||
|
|
public Attribute getAttributeIdentifier(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).getIdentifier();
|
||
|
|
}//getAttributeIdentifier()//
|
||
|
|
/**
|
||
|
|
* Gets the name of the attribute associated with the given attribute number.
|
||
|
|
* <code>
|
||
|
|
* AttributeSupport.getAttributeName(MyEntity.class, MyEntity.MY_ATTRIBUTE);
|
||
|
|
* </code>
|
||
|
|
* @param type The class that defines (or inherits from the class that defines) the attribute.
|
||
|
|
* @param attributeNumber The number that was assigned to the attribute.
|
||
|
|
* @return The name of the attribute.
|
||
|
|
*/
|
||
|
|
public static String getAttributeName(Class type, int attributeNumber) {
|
||
|
|
IList names = getAttributeNames(type);
|
||
|
|
|
||
|
|
if(names.getSize() <= attributeNumber) {
|
||
|
|
throw new RuntimeException("Unable to access an attribute's name when the attribute has not been defined. Class: " + type.getName() + " attribute number: " + attributeNumber);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return (String) names.get(attributeNumber);
|
||
|
|
}//getAttributeName()//
|
||
|
|
/**
|
||
|
|
* Gets the number of the attribute associated with the given attribute name.
|
||
|
|
* <code>
|
||
|
|
* AttributeSupport.getAttributeNumber(MyEntity.class, "myAttribute");
|
||
|
|
* </code>
|
||
|
|
* @param type The class that defines (or inherits from the class that defines) the attribute.
|
||
|
|
* @param attributeName The unique (within the type hierarchy) name of the attribute.
|
||
|
|
* @return The unqiue attribute number, or -1 if the attribute could not be identified.
|
||
|
|
*/
|
||
|
|
public static int getAttributeNumber(Class type, String attributeName) {
|
||
|
|
return MetadataService.getSingleton().getAttributeNumber(type, attributeName);
|
||
|
|
}//getAttributeNumber()//
|
||
|
|
/**
|
||
|
|
* Gets the collection of attribute names defined by a type hierarchy.
|
||
|
|
* @param type The class whose attribute names will be retrieved.
|
||
|
|
* @return A list collection of attribute names already defined for the specified type.
|
||
|
|
* @see #getDeclaredAttributeNames(Class)
|
||
|
|
*/
|
||
|
|
public static IList getAttributeNames(Class type) {
|
||
|
|
return MetadataService.getSingleton().getAttributeNames(type);
|
||
|
|
}//getAttributeNames()//
|
||
|
|
/**
|
||
|
|
* Gets the value associated with the specified attribute.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @return The value associated with the given attribute number.
|
||
|
|
*/
|
||
|
|
public Object getAttributeValue(int attributeNumber) {
|
||
|
|
Object value = null;
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
|
||
|
|
//Perform some lock checking to provide debug output on possible threading errors.//
|
||
|
|
if(isCheckingLocks && !isReflection() && getIsObjectNew() && !getSupportedObject().getMonitor().isLockingThread()) {
|
||
|
|
Debug.log(new RuntimeException("Possible thread lock error: Detected access of a shared object's attribute without holding the object's lock."));
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
value = internalGetAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
//TODO: Is this still used?
|
||
|
|
//This will only be true if the attribute required recalculation but had yet to be initialized.//
|
||
|
|
if(value instanceof RecalculateValue) {
|
||
|
|
value = null;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If the value is loading (on another thread) then wait for the load to complete. Otherwise if the value is null then lazy load the attribute's value.//
|
||
|
|
if(value == VALUE_LOADING) {
|
||
|
|
getSupportedObject().getMonitor().suspend();
|
||
|
|
|
||
|
|
try {
|
||
|
|
while((value = internalGetAttributeValue(attributeNumber)) == VALUE_LOADING) {
|
||
|
|
synchronized(this) {
|
||
|
|
wait(1000);
|
||
|
|
}//synchronized//
|
||
|
|
}//while//
|
||
|
|
}//try//
|
||
|
|
catch(InterruptedException e) {
|
||
|
|
Debug.handle(e);
|
||
|
|
}//catch//
|
||
|
|
|
||
|
|
getSupportedObject().getMonitor().resume();
|
||
|
|
}//if//
|
||
|
|
else if(value == null) {
|
||
|
|
//Note: I believe that calculated attributes in a reflection should normally be loading the reflected calculation unless set to no-reflect.//
|
||
|
|
if(isReflection() && attributeMetadata.isReflectable()) {
|
||
|
|
value = loadReflectedAttribute(attributeNumber, attributeMetadata);
|
||
|
|
}//if//
|
||
|
|
else if(getIsCalculated(attributeNumber)) {
|
||
|
|
//Recalculate the value if it is calculated and it has been flagged by the listeners as needing to be rebuilt due to a prior change in relavent data.//
|
||
|
|
value = typeMetadata.getTypeCallbackHandler().calculatedAttributeUpdate((IEntity) getSupportedObject(), typeMetadata.getAttributeMetadata(attributeNumber).getIdentifier());
|
||
|
|
setAttributeValue(attributeNumber, value, CONTEXT_TRUSTED);
|
||
|
|
}//else if//
|
||
|
|
else if(attributeMetadata.isLazy()) {
|
||
|
|
//Lazily load values that have not been loaded yet.//
|
||
|
|
value = lazyLoadAttribute(attributeNumber, attributeMetadata);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return value instanceof NullValue ? null : value;
|
||
|
|
}//getAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Registers all the listeners for the calculated attributes.
|
||
|
|
*/
|
||
|
|
public void registerCalculatedAttributeListeners() {
|
||
|
|
for(int attributeNumber = 0; attributeNumber < currentAttributeValues.length; attributeNumber++) {
|
||
|
|
if(getIsCalculated(attributeNumber)) {
|
||
|
|
ModelListener listener = typeMetadata.getTypeCallbackHandler().calculatedAttributeRegister((IEntity) getSupportedObject(), typeMetadata.getAttributeMetadata(attributeNumber).getIdentifier(), new CalculatedAttributeListener(attributeNumber));
|
||
|
|
|
||
|
|
if(listener == null) {
|
||
|
|
AttributeMetadata metadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
|
||
|
|
Debug.log(new RuntimeException("Failed to provide a handler in the calculatedAttributeUpdate(Attribute) method of the class named " + metadata.getDeclaringType() + " for the attribute named " + metadata.getName() + "."));
|
||
|
|
listener = new ModelListener((IEntity) getSupportedObject());
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(!listener.isInitialized()) {
|
||
|
|
listener.initialize();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
for(int index = 0; index < calculatedAttributeListeners.length; index++) {
|
||
|
|
if(calculatedAttributeListeners[index] == null) {
|
||
|
|
calculatedAttributeListeners[index] = listener;
|
||
|
|
break;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//registerCalculatedAttributeListeners()//
|
||
|
|
/**
|
||
|
|
* Gets the value associated with the specified attribute.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param autoInitialize Whether the attribute's value should be automatically initialized if it has never been assigned a value.
|
||
|
|
* @param notLazyLoadedValue The value returned if the attribute was not lazy loaded.
|
||
|
|
* @return The value associated with the given attribute number.
|
||
|
|
*/
|
||
|
|
protected Object getAttributeValue(int attributeNumber, boolean autoInitialize, AttributeMetadata attributeMetadata, Object notLazyLoadedValue) {
|
||
|
|
Object value = null;
|
||
|
|
boolean isBound = attributeMetadata.isAttributeBound() || attributeMetadata.isBackReference();
|
||
|
|
|
||
|
|
value = internalGetAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
if(!isBound) {
|
||
|
|
//If the value is loading (on another thread) then wait for it to complete, otherwise only need to lazy initialize if it isn't a bound attribute.//
|
||
|
|
if(value == VALUE_LOADING) {
|
||
|
|
getSupportedObject().getMonitor().suspend();
|
||
|
|
|
||
|
|
try {
|
||
|
|
while((value = internalGetAttributeValue(attributeNumber)) == VALUE_LOADING) {
|
||
|
|
synchronized(this) {
|
||
|
|
wait(1000);
|
||
|
|
}//synchronized//
|
||
|
|
}//while//
|
||
|
|
}//try//
|
||
|
|
catch(InterruptedException e) {
|
||
|
|
Debug.handle(e);
|
||
|
|
}//catch//
|
||
|
|
|
||
|
|
getSupportedObject().getMonitor().resume();
|
||
|
|
}//if//
|
||
|
|
else if((value == null) && (attributeMetadata.isLazy()) && (autoInitialize || attributeMetadata.hasDefaultValue())) { //Always lazy load attributes that have a simple default value.//
|
||
|
|
value = lazyLoadAttribute(attributeNumber, attributeMetadata);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return value == null ? notLazyLoadedValue : value instanceof NullValue ? null : value;
|
||
|
|
}//getAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Gets the value associated with the specified attribute.
|
||
|
|
* @param attributeName The unique (for this type hierarchy) name that identifies the attribute.
|
||
|
|
* @return The value associated with the named attribute.
|
||
|
|
*/
|
||
|
|
protected Object getAttributeValue(String attributeName) {
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
return attributeMetadata != null ? getAttributeValue(attributeMetadata.getNumber()) : null;
|
||
|
|
}//getAttributeValue()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.attribute.ReflectObjectSupport#getAttributeNumber(java.lang.String)
|
||
|
|
*/
|
||
|
|
protected int getAttributeNumber(String attributeName) {
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
return attributeMetadata != null ? attributeMetadata.getNumber() : -1;
|
||
|
|
}//getAttributeNumber()//
|
||
|
|
/**
|
||
|
|
* Determines whether any attribute has been changed and is updateable in the associated repository since the last time the change flags were reset.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @return Whether any of the attribute values associated with the repository have been changed. This will always be false if the object is new.
|
||
|
|
* @see #getIsObjectChanged()
|
||
|
|
* @see #getIsObjectNew()
|
||
|
|
*/
|
||
|
|
public boolean getCanObjectUpdate() {
|
||
|
|
if((!canUpdate) && (hasChanged)) {
|
||
|
|
//Refresh bound attributes.//
|
||
|
|
refreshBoundAttributes();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return canUpdate;
|
||
|
|
}//getCanObjectUpdate()//
|
||
|
|
/**
|
||
|
|
* Gets the collection of attribute names defined by a type (excluding all inherited attribute names).
|
||
|
|
* @param type The class whose declared attribute names will be retrieved.
|
||
|
|
* @return A list collection of attribute names already defined only by the specified type.
|
||
|
|
* @see #getAttributeNames(Class)
|
||
|
|
*/
|
||
|
|
public IList getDeclaredAttributeNames() {
|
||
|
|
return supportedObject.getApplication().getMetadataService().getDeclaredAttributeNames(supportedObject.getClass());
|
||
|
|
}//getDeclaredAttributeNames()//
|
||
|
|
/**
|
||
|
|
* Gets the collection of attribute names defined by a type (excluding all inherited attribute names).
|
||
|
|
* @param type The class whose declared attribute names will be retrieved.
|
||
|
|
* @return A list collection of attribute names already defined only by the specified type.
|
||
|
|
* @see #getAttributeNames(Class)
|
||
|
|
*/
|
||
|
|
public static IList getDeclaredAttributeNames(Class type) {
|
||
|
|
return MetadataService.getSingleton().getDeclaredAttributeNames(type);
|
||
|
|
}//getDeclaredAttributeNames()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute collection of modified attributes.
|
||
|
|
* @param proxyInterface An optional interface reference if the values implementing the interface should be proxied automatically.
|
||
|
|
* @return A collection of attribute data containing all recently changed attributes. This will not include trusted changes, and it will only include changes since the last change flag reset. The return value will be null if no attribute values were changed since the last reset of the change flags.
|
||
|
|
*/
|
||
|
|
AttributeCollection getDeltaAttributeCollection(Class proxyInterface, boolean includeCalculatedAttributes, boolean includeNonReflectableAttributes) {
|
||
|
|
AttributeCollection result = null;
|
||
|
|
int changeCount = 0;
|
||
|
|
|
||
|
|
if(hasChanged) {
|
||
|
|
//Count the number of attributes that will be in the delta.//
|
||
|
|
for(int attributeNumber = 0; attributeNumber < alteredAttributeValues.length; attributeNumber++) {
|
||
|
|
//If the value was changed and it either isn't calculated or we are including calculated attributes and it either isn't non-reflectable, or we are including non-reflectable attributes.//
|
||
|
|
if((alteredAttributeValues[attributeNumber] != null) && (!getIsBackReference(attributeNumber)) && ((includeCalculatedAttributes || !getIsCalculated(attributeNumber)) && (includeNonReflectableAttributes || getIsAttributeReflectable(attributeNumber)))) {
|
||
|
|
changeCount++;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//If there is at least one attribute in the delta then generate the delta.//
|
||
|
|
if(changeCount > 0) {
|
||
|
|
int index = 0;
|
||
|
|
int[] numbers = new int[changeCount];
|
||
|
|
Object[] values = new Object[changeCount];
|
||
|
|
|
||
|
|
for(int attributeNumber = 0; attributeNumber < alteredAttributeValues.length; attributeNumber++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
|
||
|
|
//If the value was changed and it either isn't calculated or we are including calculated attributes and it either isn't non-reflectable, or we are including non-reflectable attributes.//
|
||
|
|
if((alteredAttributeValues[attributeNumber] != null) && (!attributeMetadata.isBackReference()) && ((includeCalculatedAttributes || !attributeMetadata.isCalculated()) && (includeNonReflectableAttributes || attributeMetadata.isReflectable()))) {
|
||
|
|
numbers[index] = attributeNumber;
|
||
|
|
values[index++] = getAttributeValue(attributeNumber, false, attributeMetadata, null);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
result = new AttributeCollection(numbers, values, supportedObject.getClass());
|
||
|
|
|
||
|
|
if(proxyInterface != null) {
|
||
|
|
result.proxyValues(proxyInterface);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getDeltaAttributeCollection()//
|
||
|
|
/**
|
||
|
|
* Determines whether an attribute has been changed since the last time the change flags were reset.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @return Whether the attribute value has been changed.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeChanged(int attributeNumber) {
|
||
|
|
boolean result = false;
|
||
|
|
|
||
|
|
result = alteredAttributeValues[attributeNumber] != null;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getIsAttributeChanged()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as requiring lazy initialization.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute is lazily initialized.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeLazy(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isLazy();
|
||
|
|
}//getIsAttributeLazy()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as being auto calculated.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute is auto calculated.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeCalculated(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isCalculated();
|
||
|
|
}//getIsAttributeCalculated()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as referencing a collection that requires a mapping class.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute's value is of a collection type requiring a mapping class.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeMapped(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isMapped();
|
||
|
|
}//getIsAttributeMapped()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as being part of the referencer.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute's value is wholely contained by or part of the supported object.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributePartOf(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isPartOf();
|
||
|
|
}//getIsAttributePartOf()//
|
||
|
|
/**
|
||
|
|
* Determines whether an attribute has ever been assigned a value (null or otherwise).
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @return Whether the attribute has ever held a value (null or non-null).
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeRealized(int attributeNumber) {
|
||
|
|
Object currentValue = internalGetAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
return currentValue != null && !(currentValue instanceof RecalculateValue) && (currentValue != VALUE_LOADING);
|
||
|
|
}//getIsAttributeRealized()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is bound to a repository. If it is not bound to a repository then the attribute cannot be stored in a repository and thus should not affect the canUpdate flag.
|
||
|
|
* @return Whether the attribute can be stored in a repository.
|
||
|
|
* @see #getIsObjectRepositoryBound()
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeRepositoryBound(int attributeNumber) {
|
||
|
|
return (typeRepositoryMetadata != null) && (!isReflection()) && (typeRepositoryMetadata.getAttributeRepositoryMetadata(attributeNumber) != null);
|
||
|
|
}//getIsAttributeRepositoryBound()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as requiring a weak reference wrapper.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute value is weakly referenced.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeWeak(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isWeak();
|
||
|
|
}//getIsAttributeWeak()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as not being serializable.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute value is transient, meaning it does not get automatically serialized when the rest of the attributes are serialized.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeTransient(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isTransient();
|
||
|
|
}//getIsAttributeTransient()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute value can be reflected (either a reflection will be created, or if not IReflectable the value will be referenced or serialized).
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the attribute is to be copied or reflected along with the other attributes when the containing object is reflected.
|
||
|
|
*/
|
||
|
|
public boolean getIsAttributeReflectable(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isReflectable();
|
||
|
|
}//getIsAttributeReflectable()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as referencing a collection.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute's value is of a collection type.
|
||
|
|
*/
|
||
|
|
public boolean getIsCollection(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isCollection();
|
||
|
|
}//getIsCollection()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is marked as being dependant on other attributes for its value.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the given attribute's value is based on other attribute values.
|
||
|
|
*/
|
||
|
|
public boolean getIsCalculated(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isCalculated();
|
||
|
|
}//getIsCalculated()//
|
||
|
|
/**
|
||
|
|
* Determines whether the attribute is a back reference, meaning its value is set when another object references the defining class instance as part-of.
|
||
|
|
* @param attributeNumber The number identifying which attribute should be checked.
|
||
|
|
* @return Whether the attribute's value is based on the part-of reference to the object.
|
||
|
|
*/
|
||
|
|
public boolean getIsBackReference(int attributeNumber) {
|
||
|
|
return typeMetadata.getAttributeMetadata(attributeNumber).isBackReference();
|
||
|
|
}//getIsBackReference()//
|
||
|
|
/**
|
||
|
|
* Determines whether any attribute has been changed since the last time the change flags were reset.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @return Whether any of the attribute values have been changed.
|
||
|
|
* @see #getCanObjectUpdate()
|
||
|
|
* @see #getIsObjectNew()
|
||
|
|
*/
|
||
|
|
public boolean getIsObjectChanged() {
|
||
|
|
return hasChanged;
|
||
|
|
}//getIsObjectChanged()//
|
||
|
|
/**
|
||
|
|
* Determines whether any attribute has been changed since the last time the change flags were reset.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @return Whether any of the attribute values have been changed.
|
||
|
|
* @see #getCanObjectUpdate()
|
||
|
|
* @see #getIsObjectNew()
|
||
|
|
*/
|
||
|
|
public boolean getIsVirtualObjectChanged() {
|
||
|
|
/*
|
||
|
|
boolean result = hasChanged;
|
||
|
|
|
||
|
|
//Search all part-of references.//
|
||
|
|
for(int index = 0; (!result) && (index < attributeValues.length); index++) {
|
||
|
|
if((getIsAttributePartOf(index)) && (attributeValues[index] instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(attributeValues[index].getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
if(handler != null) {
|
||
|
|
result = handler.getIsVirtualObjectChanged(attributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
*/
|
||
|
|
return getVirtualObjectChangeCounter() > 0;
|
||
|
|
}//getIsVirtualObjectChanged()//
|
||
|
|
/**
|
||
|
|
* Gets whether the object has been altered in some way.
|
||
|
|
* <p>
|
||
|
|
* The altered flag errors on the side of false positives with no possibility of a false negative.
|
||
|
|
* The intended use of the altered flag is to improve validation efficiency.
|
||
|
|
* This differs from the change flages in that it allows for false positives and can be reset by the user without fear of affecting the repository or reflection code.
|
||
|
|
* </p>
|
||
|
|
*/
|
||
|
|
public boolean getIsAltered() {
|
||
|
|
return altered;
|
||
|
|
}//getIsAltered()//
|
||
|
|
/**
|
||
|
|
* Gets whether the virtual object has been altered in some way.
|
||
|
|
* <p>
|
||
|
|
* The altered flag errors on the side of false positives with no possibility of a false negative.
|
||
|
|
* The intended use of the altered flag is to improve validation efficiency.
|
||
|
|
* This differs from the change flages in that it allows for false positives and can be reset by the user without fear of affecting the repository or reflection code.
|
||
|
|
* </p>
|
||
|
|
*/
|
||
|
|
public boolean getIsVirtualObjectAltered() {
|
||
|
|
return virtualObjectAlteredCounter != 0;
|
||
|
|
}//getIsVirtualObjectAltered()//
|
||
|
|
/**
|
||
|
|
* Resets the altered flag which is used to identify when the object may have been altered.
|
||
|
|
* <p>
|
||
|
|
* The altered flag errors on the side of false positives with no possibility of a false negative.
|
||
|
|
* The intended use of the altered flag is to improve validation efficiency.
|
||
|
|
* This differs from the change flages in that it allows for false positives and can be reset by the user without fear of affecting the repository or reflection code.
|
||
|
|
* </p>
|
||
|
|
*/
|
||
|
|
public void resetAlteredFlag() {
|
||
|
|
if(altered) {
|
||
|
|
updateVirtualObjectAlteredCounter(false, 1);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
altered = false;
|
||
|
|
}//resetAlteredFlag()//
|
||
|
|
/**
|
||
|
|
* Resets the virtual object's altered flag which is used to identify when the virtual object may have been altered.
|
||
|
|
* <p>
|
||
|
|
* The altered flag errors on the side of false positives with no possibility of a false negative.
|
||
|
|
* The intended use of the altered flag is to improve validation efficiency.
|
||
|
|
* This differs from the change flages in that it allows for false positives and can be reset by the user without fear of affecting the repository or reflection code.
|
||
|
|
* </p>
|
||
|
|
*/
|
||
|
|
public void resetVirtualObjectAlteredFlags() {
|
||
|
|
resetAlteredFlag();
|
||
|
|
|
||
|
|
//Search all part-of references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
if((getIsAttributePartOf(index)) && (value instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
if(handler != null) {
|
||
|
|
handler.resetVirtualObjectAlteredFlags(value);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//resetVirtualObjectAlteredFlags()//
|
||
|
|
/**
|
||
|
|
* Updates the virtual object alter counter.
|
||
|
|
* This differs from the change counter in that it allows for false positives and can be reset by the user without fear of affecting the repository or reflection code.
|
||
|
|
* @param isAltered Whether the update should increment the altered counter.
|
||
|
|
* @param referenceCount The number of times the alter counter should increment or decrement.
|
||
|
|
*/
|
||
|
|
public void updateVirtualObjectAlteredCounter(boolean isAltered, int referenceCount) {
|
||
|
|
if(isAltered) {
|
||
|
|
boolean notify = virtualObjectChangeCounter == 0;
|
||
|
|
|
||
|
|
virtualObjectAlteredCounter += referenceCount;
|
||
|
|
|
||
|
|
if(notify) {
|
||
|
|
//Notify the parent object if in a part-of relationship.//
|
||
|
|
if(supportedObject.getPartOfEntityCounter() > 0) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(supportedObject.getPartOfEntity().getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.updateVirtualObjectAlteredCounter(supportedObject.getPartOfEntity(), true, supportedObject.getPartOfEntityCounter());
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
/*No event firing at this time.
|
||
|
|
//Fire a virtual object change state changed event.//
|
||
|
|
eventSupport.fireEvent(Entity.VIRTUAL_OBJECT_CHANGED);
|
||
|
|
*/
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
virtualObjectAlteredCounter -= referenceCount;
|
||
|
|
|
||
|
|
if(virtualObjectAlteredCounter == 0) {
|
||
|
|
//Notify the parent object if in a part-of relationship.//
|
||
|
|
if(supportedObject.getPartOfEntityCounter() > 0) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(supportedObject.getPartOfEntity().getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.updateVirtualObjectAlteredCounter(supportedObject.getPartOfEntity(), false, supportedObject.getPartOfEntityCounter());
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
/*No event firing at this time.
|
||
|
|
//Fire a virtual object change state changed event.//
|
||
|
|
eventSupport.fireEvent(Entity.VIRTUAL_OBJECT_CHANGED);
|
||
|
|
*/
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//updateVirtualObjectChangeCounter()//
|
||
|
|
/**
|
||
|
|
* Determines whether the object has not previously been read from a repository which indicates whether the object is new.
|
||
|
|
* @return Whether the object has never been read from the repository and therefor likely to be a new object in the repository.
|
||
|
|
* @see #getIsObjectChanged()
|
||
|
|
* @see #getCanObjectUpdate()
|
||
|
|
*/
|
||
|
|
public boolean getIsObjectNew() {
|
||
|
|
return repositoryIdentifier == null;
|
||
|
|
}//getIsObjectNew()//
|
||
|
|
/**
|
||
|
|
* Determines whether the object is bound to a repository. If it is not bound to a repository then the object cannot be stored in a repository.
|
||
|
|
* @return Whether the object can be stored in a repository.
|
||
|
|
* @see #getIsObjectNew()
|
||
|
|
* @see #getCanObjectUpdate()
|
||
|
|
*/
|
||
|
|
public boolean getIsObjectRepositoryBound() {
|
||
|
|
return typeRepositoryMetadata != null || (typeMetadata != null && typeMetadata.getIsRepositoryBound());
|
||
|
|
}//getIsObjectRepositoryBound()//
|
||
|
|
/**
|
||
|
|
* Determines whether the object is bound to the given repository. If it is not bound to the repository then the object cannot be stored in the repository.
|
||
|
|
* @param repositoryIdentifier The identifier for the repository we are checking.
|
||
|
|
* @return Whether the object can be stored in the repository.
|
||
|
|
* @see #getIsObjectNew()
|
||
|
|
* @see #getCanObjectUpdate()
|
||
|
|
*/
|
||
|
|
public boolean getIsObjectRepositoryBound(Object repositoryIdentifier) {
|
||
|
|
boolean result = false;
|
||
|
|
|
||
|
|
if(repositoryIdentifier == null) {
|
||
|
|
result = getIsObjectRepositoryBound();
|
||
|
|
}//if//
|
||
|
|
else if(typeRepositoryMetadata != null) {
|
||
|
|
result = repositoryIdentifier.equals(typeRepositoryMetadata.getRepositoryIdentifier());
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
result = typeMetadata.getIsRepositoryBound(repositoryIdentifier);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getIsObjectRepositoryBound()//
|
||
|
|
/**
|
||
|
|
* Gets the pre-change value for the attribute. This will be null if the attribute has not been changed since last clearing the change flags.
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @return The original value assigned to the attribute, or the value at the time of the last change flag reset.
|
||
|
|
* @see #getIsAttributeChanged(int)
|
||
|
|
*/
|
||
|
|
public Object getOldAttributeValue(int attributeNumber) {
|
||
|
|
Object value = null;
|
||
|
|
|
||
|
|
value = alteredAttributeValues[attributeNumber] != null || currentAttributeValues[attributeNumber] == NULL_VALUE ? null : currentAttributeValues[attributeNumber];
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}//getOldAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Gets the repository identifier for the repository that the entity was read from.
|
||
|
|
* @return The entity's associated repository identifier, or null if the entity was not previously read from the repository.
|
||
|
|
*/
|
||
|
|
public Object getRepositoryIdentifier() {
|
||
|
|
return repositoryIdentifier;
|
||
|
|
}//getRepositoryIdentifier()//
|
||
|
|
/**
|
||
|
|
* Gets the type metadata for the supported object.
|
||
|
|
* @return The metadata for the supported class.
|
||
|
|
*/
|
||
|
|
public TypeMetadata getTypeMetadata() {
|
||
|
|
return typeMetadata;
|
||
|
|
}//getTypeMetadata()//
|
||
|
|
/**
|
||
|
|
* Gets the type metadata for the repository from which this object was read or created, or null if this object was not read from a repository or created in a repository.
|
||
|
|
* @return The metadata for the supported class and the repository this instance exists in.
|
||
|
|
*/
|
||
|
|
public TypeRepositoryMetadata getTypeRepositoryMetadata() {
|
||
|
|
return typeRepositoryMetadata;
|
||
|
|
}//getTypeRepositoryMetadata()//
|
||
|
|
/**
|
||
|
|
* Gets an object containing all necessary state information to fully restore the state at a later time.
|
||
|
|
* @return Encapsulated state information.
|
||
|
|
* @see AttributeSupport.AttributeState#restore()
|
||
|
|
*/
|
||
|
|
public AttributeState getState() {
|
||
|
|
return new AttributeState(this);
|
||
|
|
}//getState()//
|
||
|
|
/**
|
||
|
|
* Initializes this attribute support instance.
|
||
|
|
* @param supportedObject The object being supported.
|
||
|
|
* @param eventEmitterSupport The support object used to fire attribute changed events.
|
||
|
|
*/
|
||
|
|
private void initialize(IEntity supportedObject, EventSupport eventSupport) {
|
||
|
|
this.typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
this.eventSupport = eventSupport;
|
||
|
|
this.supportedObject = supportedObject;
|
||
|
|
this.currentAttributeValues = new Object[typeMetadata.getLastAttributeNumber() + 1];
|
||
|
|
this.alteredAttributeValues = new Object[currentAttributeValues.length];
|
||
|
|
this.localChangeListeners = new LocalAttributeChangeListener[currentAttributeValues.length];
|
||
|
|
this.calculatedAttributeListeners = new ModelListener[typeMetadata.getCalculatedAttributeCount()];
|
||
|
|
resetObjectChangeFlags();
|
||
|
|
}//initialize()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.attribute.ReflectObjectSupport#initializeReflection(com.foundation.attribute.ReflectObjectData)
|
||
|
|
*/
|
||
|
|
public Object initializeReflection(ReflectObjectData reflectionData) {
|
||
|
|
Object result = null;
|
||
|
|
|
||
|
|
//Clear all attributes that are reflectable.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
if(getIsAttributeReflectable(index)) {
|
||
|
|
currentAttributeValues[index] = null;
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Clear the model listeners.//
|
||
|
|
for(int index = 0; index < calculatedAttributeListeners.length; index++) {
|
||
|
|
if(calculatedAttributeListeners[index] != null) {
|
||
|
|
calculatedAttributeListeners[index].release();
|
||
|
|
calculatedAttributeListeners[index] = null;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
canUpdate = false;
|
||
|
|
hasChanged = false;
|
||
|
|
altered = false;
|
||
|
|
virtualObjectChangeCounter = 0;
|
||
|
|
virtualObjectAlteredCounter = 0;
|
||
|
|
repositoryIdentifier = null;
|
||
|
|
typeRepositoryMetadata = null;
|
||
|
|
|
||
|
|
//Initialize the reflection.//
|
||
|
|
result = super.initializeReflection(reflectionData);
|
||
|
|
//Re-register the calculated attribute listeners.//
|
||
|
|
registerCalculatedAttributeListeners();
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//initializeReflection()//
|
||
|
|
/**
|
||
|
|
* Called after initializing this reflection.
|
||
|
|
* Allows the references to reflected objects to be replaced with their reflections.
|
||
|
|
*/
|
||
|
|
public void postInitializeReflection(ReflectObjectData reflectionData) {
|
||
|
|
if(isReflection()) {
|
||
|
|
super.postInitializeReflection(reflectionData);
|
||
|
|
}//if//
|
||
|
|
}//postInitializeReflection()//
|
||
|
|
/**
|
||
|
|
* Gets the attribute's value without any special loading or processing.
|
||
|
|
* This method allows for Reference objects to be used by the attribute system.
|
||
|
|
* @param attributeNumber The attribute whose value is to be retreived.
|
||
|
|
* @return The actual attribute value, an instance of NullValue if the value is set to null, or null if the value has not yet been set or was weak and GC'd.
|
||
|
|
*/
|
||
|
|
private Object internalGetAttributeValue(int attributeNumber) {
|
||
|
|
Object result = null;
|
||
|
|
|
||
|
|
if(alteredAttributeValues[attributeNumber] != null) {
|
||
|
|
result = alteredAttributeValues[attributeNumber];
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
result = currentAttributeValues[attributeNumber];
|
||
|
|
|
||
|
|
//Clear any soft references that are no longer active.//
|
||
|
|
if(result instanceof SoftReference) {
|
||
|
|
result = ((SoftReference) result).get();
|
||
|
|
|
||
|
|
if(result == null) {
|
||
|
|
currentAttributeValues[attributeNumber] = null;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//internalGetAttributeValue()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.attribute.AbstractReflectSupport#internalCollectReflectUpdateHandlers(com.common.util.LiteHashMap)
|
||
|
|
*/
|
||
|
|
public void internalCollectReflectUpdateHandlers(LiteHashMap reflectedMap) {
|
||
|
|
super.internalCollectReflectUpdateHandlers(reflectedMap);
|
||
|
|
|
||
|
|
//Search through the attributes for those that are part-of and reference a part-of object.//
|
||
|
|
for(int attributeIndex = 0; attributeIndex < getAttributeCount(); attributeIndex++) {
|
||
|
|
if((getIsAttributePartOf(attributeIndex)) && (getIsAttributeRealized(attributeIndex))) {
|
||
|
|
Object value = getAttributeValue(attributeIndex);
|
||
|
|
|
||
|
|
if(value instanceof IEntity) {
|
||
|
|
value = ((IEntity) value).getAttributeSupport();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(value instanceof AbstractReflectSupport) {
|
||
|
|
((AbstractReflectSupport) value).internalCollectReflectUpdateHandlers(reflectedMap);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//internalCollectReflectUpdateHandlers()//
|
||
|
|
/**
|
||
|
|
* Determines whether the two attribute values are equal.
|
||
|
|
* @param value1 The first attribute value.
|
||
|
|
* @param value2 The second attribute value.
|
||
|
|
* @return Whether the two attribute values are considered equal. This will use logical equality for objects considered to be immutable, and exact equality for IEntity instances.
|
||
|
|
*/
|
||
|
|
private boolean compareAttributeValues(Object value1, Object value2) {
|
||
|
|
boolean isEqual = false;
|
||
|
|
|
||
|
|
if(value1 instanceof NullValue) {
|
||
|
|
value1 = null;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(value2 instanceof NullValue) {
|
||
|
|
value2 = null;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
isEqual = value1 == value2;
|
||
|
|
|
||
|
|
//If the values are not exactly equal, and we are reasonably sure that the values are immutable, then we should use logical equality.//
|
||
|
|
if((!isEqual) && (!(value1 instanceof IEntity)) && (!(value2 instanceof IEntity))) {
|
||
|
|
isEqual = Comparator.equals(value1, value2);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return isEqual;
|
||
|
|
}//compareAttributeValues()//
|
||
|
|
/**
|
||
|
|
* Sets the value associated with the specified attribute.
|
||
|
|
* <p>Note: It is the caller's responsibility to synchronize if this affects a bound attribute.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param newValue The value that should be associated with this attribute.
|
||
|
|
* @param oldValue The previous value for the attribute.
|
||
|
|
* @param changeContext The context under which this attribute is being changed.
|
||
|
|
* @param updateReflections Whether the reflections should be updated. If this is false then the caller is responsible for that action.
|
||
|
|
*/
|
||
|
|
private void internalSetAttributeValue(int attributeNumber, Object newValue, Object oldValue, int changeContext, boolean updateReflections) {
|
||
|
|
Object updateNewValue = newValue;
|
||
|
|
Object updateOldValue = oldValue;
|
||
|
|
int updateFlags = 0;
|
||
|
|
int[] related = typeMetadata.getAttributeRelations(attributeNumber);
|
||
|
|
boolean isPartOf = getIsAttributePartOf(attributeNumber);
|
||
|
|
boolean refreshBoundAttributes = true;
|
||
|
|
Object secondaryOldValue = null;
|
||
|
|
boolean isCollection = getIsCollection(attributeNumber);
|
||
|
|
boolean isWeak = getIsAttributeWeak(attributeNumber);
|
||
|
|
|
||
|
|
//Ensure that if this is a collection attribute that the value must be a managed collection type.//
|
||
|
|
//Note: Collection attributes can have a null value. If the reflected object has a null value and the reflection asks for the value this would fail without allowing a null value.//
|
||
|
|
if((isCollection) && (newValue != null) && (!(newValue instanceof NullValue)) && (!(newValue instanceof ITrackedCollection))) {
|
||
|
|
Debug.log(new RuntimeException("Error: Invalid parameter type for a collection type attribute (Attribute: " + getAttributeName(attributeNumber) + "; Defining type: " + getSupported().getClass().getName() + "; Assigned value type: " + newValue.getClass().getName() + "). Expecting an " + ITrackedCollection.class.getName() + " type. Verify that the attribute should be using the registerCollection(..) method in the attribute definition."));
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Ensure the back reference attribute is not manually altered.//
|
||
|
|
if(getIsBackReference(attributeNumber) && !isTrustedChangeContext(changeContext)) {
|
||
|
|
//Debug.log(new RuntimeException("Error: Back references must be changed with a trusted context - it is not allowed for the user to change this type of attribute."));
|
||
|
|
changeContext |= CONTEXT_TRUSTED;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(isTrustedChangeContext(changeContext)) {
|
||
|
|
//Ensure the current attribute value is the old value.//
|
||
|
|
oldValue = currentAttributeValues[attributeNumber] instanceof NullValue ? null : getIsAttributeWeak(attributeNumber) && currentAttributeValues[attributeNumber] instanceof SoftReference ? ((SoftReference) currentAttributeValues[attributeNumber]).get() : currentAttributeValues[attributeNumber];
|
||
|
|
|
||
|
|
//Assign the new current value, make the reference weak if so required.//
|
||
|
|
if((allowWeakReferences()) && (newValue != null) && !(newValue instanceof NullValue) && (!isReflection()) && (getIsAttributeWeak(attributeNumber))) {
|
||
|
|
currentAttributeValues[attributeNumber] = new SoftReference(newValue);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
currentAttributeValues[attributeNumber] = newValue;
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
//Determine whether the user has altered this object.//
|
||
|
|
if(alteredAttributeValues[attributeNumber] != null) {
|
||
|
|
Object currentValue = alteredAttributeValues[attributeNumber];
|
||
|
|
|
||
|
|
//Don't refresh bound attributes since either the value hasn't really changed, or only the current value changed, but the altered value did not (which is the value that would be used).//
|
||
|
|
refreshBoundAttributes = false;
|
||
|
|
|
||
|
|
//If the new value and the altered value are the same then the users made the same changes, so clear the local change and apply the external change and fire the event with the correct flags.//
|
||
|
|
if(compareAttributeValues(alteredAttributeValues[attributeNumber], newValue)) {
|
||
|
|
//Save the old altered value which is equivalent to but not exactly equal to the new value. We might need to use it later.//
|
||
|
|
secondaryOldValue = alteredAttributeValues[attributeNumber] != newValue && !(alteredAttributeValues[attributeNumber] instanceof NullValue) ? alteredAttributeValues[attributeNumber] : null;
|
||
|
|
//Clear the altered attribute value since it is the same as (or equivalent to) the current attribute value now.//
|
||
|
|
alteredAttributeValues[attributeNumber] = null;
|
||
|
|
//Re-figure whether this object is still supposed to be marked as changed.//
|
||
|
|
recalculateChangeFlags();
|
||
|
|
updateNewValue = newValue;
|
||
|
|
updateOldValue = newValue;
|
||
|
|
updateFlags = EventSupport.FLAG_ORIGINAL_VALUE_CLEARED;
|
||
|
|
//fireUpdates(attributeNumber, newValue, newValue, updateReflections, EventSupport.FLAG_ORIGINAL_VALUE_CLEARED);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
updateNewValue = currentValue;
|
||
|
|
updateOldValue = currentValue;
|
||
|
|
updateFlags = EventSupport.FLAG_ORIGINAL_VALUE_CHANGED;
|
||
|
|
//fireUpdates(attributeNumber, currentValue, currentValue, updateReflections, EventSupport.FLAG_ORIGINAL_VALUE_CHANGED);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
updateFlags = /*isUpdate ? 0 : */EventSupport.FLAG_TRUSTED_CHANGE;
|
||
|
|
//fireUpdates(attributeNumber, oldValue, newValue, updateReflections, EventSupport.FLAG_TRUSTED_CHANGE);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//The old value is always the old altered value.//
|
||
|
|
oldValue = alteredAttributeValues[attributeNumber];
|
||
|
|
//Note: Do not create a soft reference for altered values.//
|
||
|
|
alteredAttributeValues[attributeNumber] = newValue;
|
||
|
|
|
||
|
|
//Update the altered flag.//
|
||
|
|
if(!altered) {
|
||
|
|
altered = true;
|
||
|
|
updateVirtualObjectAlteredCounter(true, 1);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If this object isn't already marked as changed then do so now.//
|
||
|
|
if(!hasChanged) {
|
||
|
|
//Set the has changed flag so we can easily tell if the object has been modified.//
|
||
|
|
hasChanged = true;
|
||
|
|
//Add one to the virtual object change counter.//
|
||
|
|
incrementVirtualObjectChangeCounter();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Set the can update flag if not already set, the object is not new, and the value set is associated with the repository.//
|
||
|
|
if((!canUpdate) && (getIsAttributeRepositoryBound(attributeNumber))) {
|
||
|
|
canUpdate = true;
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
//If the attribute value is contained (ie part of) then we need to unlink the old value, and link the new value.//
|
||
|
|
if((isCollection) || (isPartOf)) {
|
||
|
|
int isOldVirtualObjectChangedCounter = 0;
|
||
|
|
int isNewVirtualObjectChangedCounter = 0;
|
||
|
|
|
||
|
|
if((oldValue != null) && (!oldValue.equals(NULL_VALUE)) && (oldValue instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(oldValue.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.setMonitor(oldValue, null, false);
|
||
|
|
isOldVirtualObjectChangedCounter = handler.getIsVirtualObjectChanged(oldValue) ? 1 : 0;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if((secondaryOldValue != null) && (!secondaryOldValue.equals(NULL_VALUE)) && (secondaryOldValue instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(secondaryOldValue.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.setMonitor(secondaryOldValue, null, false);
|
||
|
|
|
||
|
|
if(handler.getIsVirtualObjectChanged(secondaryOldValue)) {
|
||
|
|
isOldVirtualObjectChangedCounter++;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if((newValue != null) && (!(newValue instanceof NullValue)) && (newValue instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(newValue.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.setMonitor(newValue, (IEntity) getSupportedObject(), isPartOf);
|
||
|
|
isNewVirtualObjectChangedCounter = handler.getIsVirtualObjectChanged(newValue) ? 1 : 0;
|
||
|
|
|
||
|
|
//If the attribute's value is a managed collection and it is weakly referenced then the collected values must have a back reference to their collection to prevent the collection from being GC'd while a collected value is being referenced (or some part-of component of that collected value).//
|
||
|
|
if(isCollection && isWeak) {
|
||
|
|
handler.setMaintainWeakLinkBackReferences(newValue);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Update the virtual object change counter when a part-of attribute changes value.//
|
||
|
|
// if(isOldVirtualObjectChangedCounter != isNewVirtualObjectChangedCounter) {
|
||
|
|
// if(isOldVirtualObjectChangedCounter > isNewVirtualObjectChangedCounter) {
|
||
|
|
// updateVirtualObjectChangeCounter(false, isOldVirtualObjectChangedCounter - isNewVirtualObjectChangedCounter);
|
||
|
|
// }//if//
|
||
|
|
// else {
|
||
|
|
// updateVirtualObjectChangeCounter(true, 1);
|
||
|
|
// }//else//
|
||
|
|
// }//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(refreshBoundAttributes && (!isReflection()) && (related != null) && (related.length > 0)) {
|
||
|
|
for(int index = 0; index < related.length; index++) {
|
||
|
|
refreshBoundAttribute(related[index], newValue, oldValue, isPartOf, updateReflections);
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(isUpdateChangeContext(changeContext)) {
|
||
|
|
updateFlags |= EventSupport.FLAG_REFLECTION_UPDATE;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Fire events and update listeners.//
|
||
|
|
fireUpdates(attributeNumber, updateOldValue, updateNewValue, updateReflections, updateFlags);
|
||
|
|
}//internalSetAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Recalculates whether the attributes have been changed and whether the object can be updated in the repository.
|
||
|
|
*/
|
||
|
|
private void recalculateChangeFlags() {
|
||
|
|
//TODO: We can optimize this in certain situations where the attribute reset did not map to the repository and the canUpdate is true.//
|
||
|
|
//TODO: We could optimize this furthor by replacing the can flags with counters.//
|
||
|
|
|
||
|
|
//Reset the hasChanged and canUpdate flags if necessary since the object may not be unchanged.//
|
||
|
|
if(hasChanged) {
|
||
|
|
boolean hasChanged = false;
|
||
|
|
|
||
|
|
canUpdate = false;
|
||
|
|
|
||
|
|
//Check to make sure the flag is set properly.//
|
||
|
|
for(int index = 0; (!hasChanged || !canUpdate) && (index < currentAttributeValues.length); index++) {
|
||
|
|
//If the attribute has changed then set the hasChanged flag.//
|
||
|
|
if(alteredAttributeValues[index] != null) {
|
||
|
|
hasChanged = true;
|
||
|
|
|
||
|
|
//Reset the canUpdate flag if necessary.//
|
||
|
|
if((!canUpdate) && (getIsAttributeRepositoryBound(index))) {
|
||
|
|
canUpdate = true;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
if(hasChanged != this.hasChanged) {
|
||
|
|
this.hasChanged = hasChanged;
|
||
|
|
|
||
|
|
if(hasChanged) {
|
||
|
|
incrementVirtualObjectChangeCounter();
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
decrementVirtualObjectChangeCounter();
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//recalculateChangeFlags()//
|
||
|
|
/**
|
||
|
|
* Gets the virtual object change counter.
|
||
|
|
* @return The count of part-of objects referenced by this object that are marked as having changed, plus one if this object is marked as having changed.
|
||
|
|
*/
|
||
|
|
public int getVirtualObjectChangeCounter() {
|
||
|
|
return virtualObjectChangeCounter;
|
||
|
|
}//getVirtualObjectChangeCounter()//
|
||
|
|
/**
|
||
|
|
* Increments the virtual object change counter by one and sends any necessary notifications.
|
||
|
|
*/
|
||
|
|
protected void incrementVirtualObjectChangeCounter() {
|
||
|
|
updateVirtualObjectChangeCounter(true, 1);
|
||
|
|
}//incrementVirtualObjectChangeCounter()//
|
||
|
|
/**
|
||
|
|
* Decrements the virtual object change counter by one and sends any necessary notifications.
|
||
|
|
*/
|
||
|
|
protected void decrementVirtualObjectChangeCounter() {
|
||
|
|
updateVirtualObjectChangeCounter(false, 1);
|
||
|
|
}//decrementVirtualObjectChangeCounter()//
|
||
|
|
/**
|
||
|
|
* Updates the virtual object change counter.
|
||
|
|
* @param isChanged Whether the update should increment the change counter.
|
||
|
|
* @param referenceCount The number of times the change counter should increment or decrement.
|
||
|
|
*/
|
||
|
|
public void updateVirtualObjectChangeCounter(boolean isChanged, int referenceCount) {
|
||
|
|
if(isChanged) {
|
||
|
|
boolean notify = virtualObjectChangeCounter == 0;
|
||
|
|
|
||
|
|
virtualObjectChangeCounter += referenceCount;
|
||
|
|
|
||
|
|
if(notify) {
|
||
|
|
//Notify the parent object if in a part-of relationship.//
|
||
|
|
if(supportedObject.getPartOfEntityCounter() > 0) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(supportedObject.getPartOfEntity().getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.updateVirtualObjectChangeCounter(supportedObject.getPartOfEntity(), true, supportedObject.getPartOfEntityCounter());
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Fire a virtual object change state changed event.//
|
||
|
|
eventSupport.fireEvent(IEntity.VIRTUAL_OBJECT_CHANGED);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
virtualObjectChangeCounter -= referenceCount;
|
||
|
|
|
||
|
|
if(virtualObjectChangeCounter == 0) {
|
||
|
|
//Notify the parent object if in a part-of relationship.//
|
||
|
|
if(supportedObject.getPartOfEntityCounter() > 0) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(supportedObject.getPartOfEntity().getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
handler.updateVirtualObjectChangeCounter(supportedObject.getPartOfEntity(), false, supportedObject.getPartOfEntityCounter());
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Fire a virtual object change state changed event.//
|
||
|
|
eventSupport.fireEvent(IEntity.VIRTUAL_OBJECT_CHANGED);
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//updateVirtualObjectChangeCounter()//
|
||
|
|
/**
|
||
|
|
* Registers reference bound attributes between the supported entity and its parent (which it is part of).
|
||
|
|
* @param partOfEntityAttributeSupport The entity's attribute support that this support's supported entity is a part of.
|
||
|
|
*/
|
||
|
|
public void registerReferenceBoundAttributes(AttributeSupport partOfEntityAttributeSupport) {
|
||
|
|
//Unregister old bindings.//
|
||
|
|
if(partOfAttributeBindings != null) {
|
||
|
|
for(int index = 0; index < partOfAttributeBindings.getSize(); index++) {
|
||
|
|
AttributeMetadata attributeMetadata = (AttributeMetadata) partOfAttributeBindings.get(index);
|
||
|
|
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), null, CONTEXT_UNTRUSTED);
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
partOfAttributeBindings = null;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Register new bindings.//
|
||
|
|
if(partOfEntityAttributeSupport != null) {
|
||
|
|
IEntity partOfEntity = (IEntity) partOfEntityAttributeSupport.getSupportedObject();
|
||
|
|
IList boundAttributes = typeMetadata.getBoundAttributes(partOfEntity.getClass());
|
||
|
|
|
||
|
|
if((boundAttributes != null) && (boundAttributes.getSize() > 0)) {
|
||
|
|
partOfAttributeBindings = boundAttributes;
|
||
|
|
|
||
|
|
for(int index = 0; index < boundAttributes.getSize(); index++) {
|
||
|
|
AttributeMetadata attributeMetadata = (AttributeMetadata) boundAttributes.get(index);
|
||
|
|
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), partOfEntity, CONTEXT_UNTRUSTED);
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//registerReferenceBoundAttributes()//
|
||
|
|
/**
|
||
|
|
* Fires events as necessary saying that the attribute's value has been altered.
|
||
|
|
* @param attributeNumber The number for the attribute that was changed.
|
||
|
|
* @param oldValue The old value for the attribute.
|
||
|
|
* @param newValue The new value for the attribute.
|
||
|
|
* @param updateReflections Whether the reflections of this object should be notified.
|
||
|
|
* @param flags Zero, or one of the flags defined by EventSupport.
|
||
|
|
*/
|
||
|
|
protected void fireUpdates(int attributeNumber, Object oldValue, Object newValue, boolean updateReflections, int flags) {
|
||
|
|
//If we have an event emitter support then fire the attribute changed event.//
|
||
|
|
if(eventSupport != null) {
|
||
|
|
//We don't send the values because we don't know whether to proxy the values for remote events and we don't know if they are really needed.//
|
||
|
|
if(eventSupport.hasListeners(attributeNumber)) {
|
||
|
|
eventSupport.fireEvent(attributeNumber, true, flags);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Notify the local change listeners. These are all inline and internal listeners for attribute changes. There normally should be none and should rarely be more than one.//
|
||
|
|
if(localChangeListeners[attributeNumber] != null) {
|
||
|
|
LocalAttributeChangeListener listenerNode = localChangeListeners[attributeNumber];
|
||
|
|
|
||
|
|
while(listenerNode != null) {
|
||
|
|
listenerNode.listener.attributeChanged(oldValue, newValue);
|
||
|
|
listenerNode = listenerNode.next;
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//We may not always update the reflections at this time. When synchronizing data from a reflection we will want to optimize the updates of other reflections by sending a complete update instead of many partial updates.//
|
||
|
|
if(updateReflections) {
|
||
|
|
//Update any reflections.//
|
||
|
|
updateReflections(attributeNumber);
|
||
|
|
}//if//
|
||
|
|
}//fireUpdates()//
|
||
|
|
/**
|
||
|
|
* Adds an attribute change listener which will get notified of changes to the selected attribute.
|
||
|
|
* @param attribute The attribute to be listened to.
|
||
|
|
* @param listener The listener to be notified of the value changes.
|
||
|
|
*/
|
||
|
|
protected void addAttributeChangeListener(int attribute, IAttributeChangeListener listener) {
|
||
|
|
localChangeListeners[attribute] = new LocalAttributeChangeListener(localChangeListeners[attribute], listener);
|
||
|
|
}//addAttributeChangeListener()//
|
||
|
|
/**
|
||
|
|
* Removes an attribute change listener.
|
||
|
|
* @param attribute The attribute to stop being listened to.
|
||
|
|
* @param listener The listener to be removed.
|
||
|
|
*/
|
||
|
|
protected void removeAttributeChangeListener(int attribute, IAttributeChangeListener listener) {
|
||
|
|
LocalAttributeChangeListener next = localChangeListeners[attribute];
|
||
|
|
LocalAttributeChangeListener previous = null;
|
||
|
|
|
||
|
|
while(next != null) {
|
||
|
|
if(next.listener == listener) {
|
||
|
|
if(previous != null) {
|
||
|
|
previous.next = next.next;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
localChangeListeners[attribute] = next.next;
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
break;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
previous = next;
|
||
|
|
next = next.next;
|
||
|
|
}//while//
|
||
|
|
}//removeAttributeChangeListener()//
|
||
|
|
/**
|
||
|
|
* Determines whether the object should utilize weak references.
|
||
|
|
* @return Whether the attributes should weakly reference attribute values with the proper flag.
|
||
|
|
*/
|
||
|
|
protected boolean allowWeakReferences() {
|
||
|
|
return suspendWeakReferencesCount == 0 && !isReflection() && !getIsObjectNew();
|
||
|
|
}//allowWeakReferences()//
|
||
|
|
/**
|
||
|
|
* Updates the attributes to weakly reference those attributes marked as requiring it.
|
||
|
|
* This is used after creating an object in the repository since the object will not weakly reference the attribute values until it has been placed in a repository and can resurect the data.
|
||
|
|
*/
|
||
|
|
public void updateWeakReferences() {
|
||
|
|
if(allowWeakReferences()) {
|
||
|
|
for(int attributeNumber = 0; attributeNumber < currentAttributeValues.length; attributeNumber++) {
|
||
|
|
if(getIsAttributeRealized(attributeNumber)) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
|
||
|
|
if(attributeMetadata.isWeak() && !(currentAttributeValues[attributeNumber] instanceof NullValue) && !(currentAttributeValues[attributeNumber] instanceof SoftReference)) {
|
||
|
|
currentAttributeValues[attributeNumber] = new SoftReference(currentAttributeValues[attributeNumber]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//updateWeakReferences()//
|
||
|
|
/**
|
||
|
|
* Sets a custom lazy load handler for a specific attribute.
|
||
|
|
* @param attribute The attribute to have the handler. This attribute should be flagged as lazily loaded, or have a default value set, otherwise the handler will never be called. Reflections will ignore all lazy loading since they always call the reflected object for the value, unless the attribute is marked as not being reflected.
|
||
|
|
* @param handler The handler called to lazily load the attribute.
|
||
|
|
* @return The old custom lazy load handler, or null if an old handler didn't exist.
|
||
|
|
*/
|
||
|
|
public ICustomLazyLoadHandler setCustomLazyLoadHandler(Attribute attribute, ICustomLazyLoadHandler handler) {
|
||
|
|
ICustomLazyLoadHandler result;
|
||
|
|
|
||
|
|
if(customLazyLoadHandlers == null) {
|
||
|
|
customLazyLoadHandlers = new ICustomLazyLoadHandler[currentAttributeValues.length];
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
result = customLazyLoadHandlers[attribute.getNumber()];
|
||
|
|
customLazyLoadHandlers[attribute.getNumber()] = handler;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//setCustomLazyLoadHandler()//
|
||
|
|
/**
|
||
|
|
* Lazily initialize the attribute's value. The attribute must be lazy loadable.
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param metadata The attribute's metadata.
|
||
|
|
* @return The value associated with the given attribute number.
|
||
|
|
*/
|
||
|
|
private Object lazyLoadAttribute(int attributeNumber, AttributeMetadata metadata) {
|
||
|
|
Object value = null;
|
||
|
|
|
||
|
|
//First check for a custom lazy load handler which will override the default value or standard lazy load handlers.//
|
||
|
|
if(customLazyLoadHandlers != null && customLazyLoadHandlers[attributeNumber] != null) {
|
||
|
|
//Flag the attribute as being loaded so that other threads that might sneak in while this thread is performing the lazy load don't also try to load the attribute.//
|
||
|
|
currentAttributeValues[attributeNumber] = VALUE_LOADING;
|
||
|
|
//Call the custom lazy load handler for the attribute.//
|
||
|
|
value = customLazyLoadHandlers[attributeNumber].lazyLoadAttribute(getSupported(), getAttributeIdentifier(attributeNumber));
|
||
|
|
//Note: We have to fire events for this because the model listener class depends on knowing when the attribute has been assigned a value.//
|
||
|
|
internalSetAttributeValue(attributeNumber, value == null ? NULL_VALUE : value, null, CONTEXT_TRUSTED, false);
|
||
|
|
|
||
|
|
//Notify any threads that might be waiting for the attribute's value to be loaded.//
|
||
|
|
synchronized(this) {
|
||
|
|
notifyAll();
|
||
|
|
}//synchronized//
|
||
|
|
}//if//
|
||
|
|
else if(metadata.hasDefaultValue()) {
|
||
|
|
value = metadata.getDefaultValue();
|
||
|
|
internalSetAttributeValue(attributeNumber, value == null ? NULL_VALUE : value, null, CONTEXT_TRUSTED, false);
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
//Flag the attribute as being loaded so that other threads that might sneak in while this thread is performing the lazy load don't also try to load the attribute.//
|
||
|
|
currentAttributeValues[attributeNumber] = VALUE_LOADING;
|
||
|
|
//Call the lazy load handler for the attribute.//
|
||
|
|
value = typeMetadata.getTypeCallbackHandler().lazyLoadAttribute((IEntity) getSupportedObject(), typeMetadata.getAttributeMetadata(attributeNumber).getIdentifier());
|
||
|
|
//Note: We have to fire events for this because the model listener class depends on knowing when the attribute has been assigned a value.//
|
||
|
|
internalSetAttributeValue(attributeNumber, value == null ? NULL_VALUE : value, null, CONTEXT_TRUSTED, false);
|
||
|
|
|
||
|
|
//Notify any threads that might be waiting for the attribute's value to be loaded.//
|
||
|
|
synchronized(this) {
|
||
|
|
notifyAll();
|
||
|
|
}//synchronized//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}//lazyLoadAttribute()//
|
||
|
|
/**
|
||
|
|
* Lazily initializes the attribute's value from the reflected object. The attribute must be reflectable and must currently have a value of null indicating it has not yet been reflected.
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param metadata The attribute's metadata.
|
||
|
|
* @return The value associated with the given attribute number.
|
||
|
|
*/
|
||
|
|
private Object loadReflectedAttribute(int attributeNumber, AttributeMetadata metadata) {
|
||
|
|
Object value = null;
|
||
|
|
|
||
|
|
//Send a message to the reflected object to load the attribute.//
|
||
|
|
try {
|
||
|
|
//Flag the attribute as being loaded so that other threads that might sneak in while this thread is performing the lazy load don't also try to load the attribute.//
|
||
|
|
currentAttributeValues[attributeNumber] = VALUE_LOADING;
|
||
|
|
//Load the reflected attribute's value.//
|
||
|
|
value = loadReflectionAttribute(metadata.getName());
|
||
|
|
}//try//
|
||
|
|
catch(InstantiationException e) {
|
||
|
|
Debug.log(e); //TODO: What should we do here?//
|
||
|
|
}//catch//
|
||
|
|
catch(IllegalAccessException e) {
|
||
|
|
Debug.log(e); //TODO: What should we do here?//
|
||
|
|
}//catch//
|
||
|
|
|
||
|
|
//Note: I changed this to fire events because the model listeners were not being updated if events were not fired. We could set this back to false if model listeners used some other event mechanism.//
|
||
|
|
//Save the value and don't fire any events at all since this value should have always existed.//
|
||
|
|
//TODO: This could cause some trouble if the getter is never called but a listener is setup for the attribute's changes. However in such a case would it really matter if the event is fired?
|
||
|
|
internalSetAttributeValue(attributeNumber, value == null ? NULL_VALUE : value, null, CONTEXT_TRUSTED, false);
|
||
|
|
|
||
|
|
//Notify any threads that might be waiting for the attribute's value to be loaded.//
|
||
|
|
synchronized(this) {
|
||
|
|
notifyAll();
|
||
|
|
}//synchronized//
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}//loadReflectedAttribute()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(com.common.io.IObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
byte version = in.readByte(); //Version//
|
||
|
|
|
||
|
|
//TODO: Remove the old version code at a suitable time (9/2005).
|
||
|
|
if(version == 0) {
|
||
|
|
String attributeName = null;
|
||
|
|
Object attributeValue = null;
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
int attributeCount;
|
||
|
|
|
||
|
|
attributeCount = in.readInt();
|
||
|
|
|
||
|
|
while(attributeCount > 0) {
|
||
|
|
attributeName = in.readUTF();
|
||
|
|
attributeValue = in.readObject();
|
||
|
|
|
||
|
|
//Fix the attribute value by localizing it if possible.//
|
||
|
|
if(in.readBoolean()) {
|
||
|
|
if((Orb.isProxy(attributeValue)) && (Orb.isLocal(attributeValue))) {
|
||
|
|
attributeValue = Orb.getLocal(attributeValue);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
//Sometimes the application versions don't match and we have to ignore certain attributes.//
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), attributeValue, CONTEXT_TRUSTED);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeCount--;
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
String attributeName = (String) in.readObject();
|
||
|
|
Object attributeValue = null;
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
|
||
|
|
//Read each attribute from the stream.//
|
||
|
|
while(attributeName != null) {
|
||
|
|
attributeValue = in.readObject();
|
||
|
|
|
||
|
|
//Fix the attribute value by localizing it if possible.//
|
||
|
|
if(in.readBoolean()) {
|
||
|
|
if((Orb.isProxy(attributeValue)) && (Orb.isLocal(attributeValue))) {
|
||
|
|
attributeValue = Orb.getLocal(attributeValue);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
//Sometimes the application versions don't match and we have to ignore certain attributes.//
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), attributeValue, CONTEXT_TRUSTED);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeName = (String) in.readObject();
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
if(version > 1) {
|
||
|
|
setModelMappingNumber(in.readInt());
|
||
|
|
setHash(in.readInt());
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Reads the object from a stream.
|
||
|
|
* @param in The input stream to read from.
|
||
|
|
*/
|
||
|
|
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
|
||
|
|
byte version = in.readByte(); //Version//
|
||
|
|
|
||
|
|
//TODO: Remove the old version code at a suitable time (9/2005).
|
||
|
|
if(version == 0) {
|
||
|
|
String attributeName = null;
|
||
|
|
Object attributeValue = null;
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
int attributeCount;
|
||
|
|
|
||
|
|
//Determine the count of attributes on the stream.//
|
||
|
|
attributeCount = in.readInt();
|
||
|
|
|
||
|
|
//Read each attribute from the stream.//
|
||
|
|
while(attributeCount > 0) {
|
||
|
|
attributeName = in.readUTF();
|
||
|
|
attributeValue = in.readObject();
|
||
|
|
|
||
|
|
//Fix the attribute value by localizing it if possible.//
|
||
|
|
if(in.readBoolean()) {
|
||
|
|
if((Orb.isProxy(attributeValue)) && (Orb.isLocal(attributeValue))) {
|
||
|
|
attributeValue = Orb.getLocal(attributeValue);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//attributeChangeFlag = in.readBoolean();
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
//Sometimes the application versions don't match and we have to ignore certain attributes.//
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), attributeValue, CONTEXT_TRUSTED);
|
||
|
|
//changeFlags[attributeMetadata.getNumber()] = attributeChangeFlag;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeCount--;
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
String attributeName = (String) in.readObject();
|
||
|
|
Object attributeValue = null;
|
||
|
|
TypeMetadata typeMetadata = MetadataService.getSingleton().getTypeMetadata(supportedObject.getClass());
|
||
|
|
AttributeMetadata attributeMetadata = null;
|
||
|
|
|
||
|
|
//Read each attribute from the stream.//
|
||
|
|
while(attributeName != null) {
|
||
|
|
attributeValue = in.readObject();
|
||
|
|
|
||
|
|
//Fix the attribute value by localizing it if possible.//
|
||
|
|
if(in.readBoolean()) {
|
||
|
|
if((Orb.isProxy(attributeValue)) && (Orb.isLocal(attributeValue))) {
|
||
|
|
attributeValue = Orb.getLocal(attributeValue);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeMetadata = typeMetadata.getAttributeMetadata(attributeName);
|
||
|
|
|
||
|
|
//Sometimes the application versions don't match and we have to ignore certain attributes.//
|
||
|
|
if(attributeMetadata != null) {
|
||
|
|
setAttributeValue(attributeMetadata.getNumber(), attributeValue, CONTEXT_TRUSTED);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
attributeName = (String) in.readObject();
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
if(version > 1) {
|
||
|
|
setModelMappingNumber(in.readInt());
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
}//readExternal()//
|
||
|
|
/**
|
||
|
|
* Refreshes the bound attribute's value given a new related value.
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the bound attribute.
|
||
|
|
* @param newRelatedValue The new value that this bound attribute is related to. The new value for this attribute should be the new related value's bound attribute value.
|
||
|
|
* @param oldRelatedValue The old value that this bound attribute was related to.
|
||
|
|
* @param isPartOf Whether the related attribute's value is a part of this object. Only objects with a part of relationship will register change listeners. Standard reference relationships must have already set the necessary values and it is assumed they will not change.
|
||
|
|
* @param updateReflections Whether the reflections should be updated. If this is false then the caller is responsible for that action.
|
||
|
|
*/
|
||
|
|
private void refreshBoundAttribute(int attributeNumber, Object newRelatedValue, Object oldRelatedValue, boolean isPartOf, boolean updateReflections) {
|
||
|
|
int boundAttribute = typeMetadata.getAttributeMetadata(attributeNumber).getBoundAttribute();
|
||
|
|
Object value = null;
|
||
|
|
|
||
|
|
if((newRelatedValue != null) && (!newRelatedValue.equals(NULL_VALUE))) {
|
||
|
|
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(newRelatedValue.getClass());
|
||
|
|
|
||
|
|
if(metadata != null) {
|
||
|
|
value = metadata.getTypeCallbackHandler().getAttributeSupport(newRelatedValue).getAttributeValue(boundAttribute);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Note: We are not going to synchronize on the related object because it will either be a part of this object, or it will only be accessable to this thread, or the object's value should have been set prior to having set the related attribute value.//
|
||
|
|
//Also note that attribute bindings should only be used against semi-immutable attribute values. The bound value should never change unless it was never set, in which case the object was new and the value was set while creating it in the repository, before the object was shared.//
|
||
|
|
|
||
|
|
setAttributeValue(attributeNumber, value, CONTEXT_UNTRUSTED, updateReflections);
|
||
|
|
}//refreshBoundAttribute()//
|
||
|
|
/**
|
||
|
|
* Refreshes all bound attributes that have not been assigned a value and whose related attribute is not null.
|
||
|
|
* @return Whether any attributes required refreshing.
|
||
|
|
*/
|
||
|
|
public boolean refreshBoundAttributes() {
|
||
|
|
int lastAttribute = typeMetadata.getLastAttributeNumber();
|
||
|
|
boolean result = false;
|
||
|
|
|
||
|
|
//Check each attribute to see if it is bound and if it is then try to set the bound value.//
|
||
|
|
for(int index = 0; index <= lastAttribute; index++) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(index);
|
||
|
|
Object relatedValue = null;
|
||
|
|
|
||
|
|
//If the attributeMetadata is not yet set, is bound, and there is a non-null related value, then try to get the bound value.//
|
||
|
|
if(attributeMetadata.isAttributeBound()) {
|
||
|
|
Object nextAttributeValue = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
if(((nextAttributeValue == null) || (nextAttributeValue instanceof NullValue)) && ((relatedValue = getAttributeValue(attributeMetadata.getRelatedAttribute(), false, attributeMetadata, null)) != null)) {
|
||
|
|
int boundAttribute = attributeMetadata.getBoundAttribute();
|
||
|
|
TypeMetadata relatedMetadata = MetadataService.getSingleton().getTypeMetadata(relatedValue.getClass());
|
||
|
|
|
||
|
|
//If we can find metadata for the related value then get the bound value.//
|
||
|
|
if(relatedMetadata != null) {
|
||
|
|
Object value = relatedMetadata.getTypeCallbackHandler().getAttributeSupport(relatedValue).getAttributeValue(boundAttribute);
|
||
|
|
//TODO: Should allow for proxy values here. Use an agent to keep from using a public method on the entity.//
|
||
|
|
|
||
|
|
if(value != null) {
|
||
|
|
//Note: We are not going to synchronize on the related object because it will either be a part of this object, or it will only be accessable to this thread, or the object's value should have been set prior to having set the related attributeMetadata value.//
|
||
|
|
//Also note that attributeMetadata bindings should only be used against semi-immutable attributeMetadata values. The bound value should never change unless it was never set, in which case the object was new and the value was set while creating it in the repository, before the object was shared.//
|
||
|
|
|
||
|
|
//Set the new bound attributeMetadata value.//
|
||
|
|
setAttributeValue(index, value, CONTEXT_UNTRUSTED);
|
||
|
|
//Set the return flag.//
|
||
|
|
result = true;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//refreshBoundAttributes()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic events to register those events in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the event which can be used for accessing the event.
|
||
|
|
* @return The unique (for the declaring type hierarchy) event identifier which can be used to access the event.
|
||
|
|
*/
|
||
|
|
public static Event registerEvent(Class declaringType, String name) {
|
||
|
|
return MetadataService.getSingleton().addEvent(declaringType, name);
|
||
|
|
}//registerEvent()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name) {
|
||
|
|
return registerAttribute(declaringType, name, OPTION_REFERENCED);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param options The options associated with the registered attribute.
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
* @see #OPTION_REFERENCED
|
||
|
|
* @see #OPTION_PART_OF
|
||
|
|
* @see #OPTION_LAZY
|
||
|
|
* @see #OPTION_WEAK
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, int options) {
|
||
|
|
return registerAttribute(declaringType, name, options, null);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param options The options associated with the registered attribute.
|
||
|
|
* @param valueType The optional value type. This may be required depending on the options selected. The value type can be used by the transaction and other systems to create instances that will be valid for the attribute.
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
* @see #OPTION_REFERENCED
|
||
|
|
* @see #OPTION_PART_OF
|
||
|
|
* @see #OPTION_LAZY
|
||
|
|
* @see #OPTION_WEAK
|
||
|
|
* @see #OPTION_VERSION_REFERENCE
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, int options, Class valueType) {
|
||
|
|
//Make sure that the attribute is not listed as a collection type.//
|
||
|
|
if((options & OPTION_COLLECTION) > 0) {
|
||
|
|
options = options ^ OPTION_COLLECTION;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//TODO: Should check to make sure that the attribute is not referenced and part of.//
|
||
|
|
|
||
|
|
return MetadataService.getSingleton().addAttribute(declaringType, name, options, valueType);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param options The options associated with the registered attribute.
|
||
|
|
* @param defaultValue The immutable value to be used if the attribute doesn't have a value. This should only be used for immutable objects such as BigDecimal, Integer, String, etc., or objects that will only be used in an immutable fashion (usually java.util.Date falls into this category).
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
* @see #OPTION_REFERENCED
|
||
|
|
* @see #OPTION_PART_OF
|
||
|
|
* @see #OPTION_LAZY
|
||
|
|
* @see #OPTION_WEAK
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, int options, Object defaultValue) {
|
||
|
|
return registerAttribute(declaringType, name, options, null, defaultValue);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param options The options associated with the registered attribute.
|
||
|
|
* @param valueType The optional value type. This may be required depending on the options selected. The value type can be used by the transaction and other systems to create instances that will be valid for the attribute.
|
||
|
|
* @param defaultValue The immutable value to be used if the attribute doesn't have a value. This should only be used for immutable objects such as BigDecimal, Integer, String, etc., or objects that will only be used in an immutable fashion (usually java.util.Date falls into this category).
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
* @see #OPTION_REFERENCED
|
||
|
|
* @see #OPTION_PART_OF
|
||
|
|
* @see #OPTION_LAZY
|
||
|
|
* @see #OPTION_WEAK
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, int options, Class valueType, Object defaultValue) {
|
||
|
|
//Make sure that the attribute is not listed as a collection type.//
|
||
|
|
if((options & OPTION_COLLECTION) > 0) {
|
||
|
|
options = options ^ OPTION_COLLECTION;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//TODO: Should check to make sure that the attribute is not referenced and part of.//
|
||
|
|
|
||
|
|
return MetadataService.getSingleton().addAttribute(declaringType, name, options, valueType, defaultValue);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* This variation of the register method allows the value of the attribute to be bound to a specific attribute of another object referenced or contained by the registering class.
|
||
|
|
* The related attribute's value will be queried for the bound attribute's value.
|
||
|
|
* This attribute being registered will always reference the bound attribute's value, or if the related attribute's value is null then this attribute's value will be null.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param relatedAttributeNumber The attribute in the same class to which this attribute's value is related.
|
||
|
|
* @param boundAttributeNumber The attribute on the related attribute's value to which this attribute value is bound (or identitical).
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, int relatedAttributeNumber, int boundAttributeNumber) {
|
||
|
|
return MetadataService.getSingleton().addAttribute(declaringType, name, relatedAttributeNumber, boundAttributeNumber);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* This variation of the register method allows the value of the attribute to be bound to a specific attribute of another object referenced or contained by the registering class.
|
||
|
|
* The related attribute's value will be queried for the bound attribute's value.
|
||
|
|
* This attribute being registered will always reference the bound attribute's value, or if the related attribute's value is null then this attribute's value will be null.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param boundType The class of referencing object (part-of only) that will set the attribute's value (referencing object will be the value).
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
*/
|
||
|
|
public static Attribute registerAttribute(Class declaringType, String name, Class boundType) {
|
||
|
|
return MetadataService.getSingleton().addAttribute(declaringType, name, boundType);
|
||
|
|
}//registerAttribute()//
|
||
|
|
/**
|
||
|
|
* Provides a simple mechanism for a class using dynamic attributes to register those attributes in the class's static initializers.
|
||
|
|
* @param declaringType The class that is registering the attribute.
|
||
|
|
* @param name The name of the attribute which can be used for accessing the attribute value.
|
||
|
|
* @param options The options associated with the registered attribute.
|
||
|
|
* @return The unique (for the declaring type hierarchy) attribute identifier which can be used to access the attribute value.
|
||
|
|
* @see #OPTION_REFERENCED
|
||
|
|
* @see #OPTION_PART_OF
|
||
|
|
* @see #OPTION_LAZY
|
||
|
|
* @see #OPTION_WEAK
|
||
|
|
*/
|
||
|
|
public static Attribute registerCollection(Class declaringType, String name, int options) {
|
||
|
|
//Make sure that the attribute is listed as a collection type.//
|
||
|
|
options = options | OPTION_COLLECTION;
|
||
|
|
|
||
|
|
//TODO: Should check to make sure that the attribute is not referenced and part of.//
|
||
|
|
|
||
|
|
return MetadataService.getSingleton().addAttribute(declaringType, name, options, null);
|
||
|
|
}//registerCollection()//
|
||
|
|
/**
|
||
|
|
* Sets the debug flag to turn on checking of locks.
|
||
|
|
* If this is turned on then the system will verify that the calling thread holds a lock on the model every time an attribute is accessed.
|
||
|
|
* If the model is new (not in the repository) or is a reflection, the check will not occur.
|
||
|
|
*/
|
||
|
|
public static void checkLocks() {
|
||
|
|
isCheckingLocks = true;
|
||
|
|
}//checkLocks()//
|
||
|
|
/**
|
||
|
|
* Determines whether the system should be checking locks.
|
||
|
|
* If this is turned on then the system will verify that the calling thread holds a lock on the model every time an attribute is accessed.
|
||
|
|
* If the model is new (not in the repository) or is a reflection, the check will not occur.
|
||
|
|
*/
|
||
|
|
public static boolean isCheckingLocks() {
|
||
|
|
return isCheckingLocks;
|
||
|
|
}//isCheckingLocks()//
|
||
|
|
/**
|
||
|
|
* Suspends processing of calculated attribute updates either entirely, or until the resume method is called.
|
||
|
|
* @param updateOnResume Whether the resume should update the calculated attributes.
|
||
|
|
* @see #resumeCalculatedAttributeUpdates()
|
||
|
|
*/
|
||
|
|
public static void suspendCalculatedAttributeUpdates(boolean updateOnResume) {
|
||
|
|
SuspendCalculatedAttributesData data = (SuspendCalculatedAttributesData) calculatedAttributeSuspendData.get();
|
||
|
|
|
||
|
|
if(data == null) {
|
||
|
|
calculatedAttributeSuspendData.set(new SuspendCalculatedAttributesData(updateOnResume));
|
||
|
|
}//if//
|
||
|
|
else if((updateOnResume && (data.getCalculatedAttributeListenerSet() != null)) || (!updateOnResume && (data.getCalculatedAttributeListenerSet() == null))) {
|
||
|
|
data.setSuspendCount(data.getSuspendCount() + 1);
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
Debug.log(new RuntimeException(), "Cannot recursively suspend or delay calculated attribute updates without the exact same parameters.");
|
||
|
|
}//else//
|
||
|
|
}//suspendCalculatedAttributeUpdates()//
|
||
|
|
/**
|
||
|
|
* Resumes processing of calculated attribute updates.
|
||
|
|
*/
|
||
|
|
public static void resumeCalculatedAttributeUpdates() {
|
||
|
|
SuspendCalculatedAttributesData suspendData = (SuspendCalculatedAttributesData) calculatedAttributeSuspendData.get();
|
||
|
|
|
||
|
|
if(suspendData != null) {
|
||
|
|
if(suspendData.getSuspendCount() == 1) {
|
||
|
|
//If we collected suspended updates then perform them now.//
|
||
|
|
if(suspendData.getCalculatedAttributeListenerSet() != null) {
|
||
|
|
IHashSet suspendedListeners = suspendData.getCalculatedAttributeListenerSet();
|
||
|
|
|
||
|
|
//Keep processing the suspended listeners until they have all been updated. This hash set remains in the thread local durring this proceedure to ensure that calculated values that are part of other calculated values don't cause the same value to be calculated repeatedly.//
|
||
|
|
while(suspendedListeners.getSize() > 0) {
|
||
|
|
Object[] listeners = suspendedListeners.toArray();
|
||
|
|
|
||
|
|
suspendedListeners.removeAll();
|
||
|
|
|
||
|
|
for(int index = 0; index < listeners.length; index++) {
|
||
|
|
((CalculatedAttributeListener) listeners[index]).updateCalculatedAttribute();
|
||
|
|
}//for//
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
calculatedAttributeSuspendData.set(null);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
suspendData.setSuspendCount(suspendData.getSuspendCount() - 1);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
Debug.log(new RuntimeException(), "Resumption of calculated attribute updates occured one too many times.");
|
||
|
|
}//else//
|
||
|
|
}//resumeCalculatedAttributeUpdates()//
|
||
|
|
/**
|
||
|
|
* Resets all the change flags.
|
||
|
|
*/
|
||
|
|
public void resetObjectChangeFlags() {
|
||
|
|
//Synchronized to avoid problems relating to bound attributes. This shouldn't cost much to do.//
|
||
|
|
for(int index = currentAttributeValues.length - 1; index >= 0; index--) {
|
||
|
|
if(alteredAttributeValues[index] != null) {
|
||
|
|
if(getIsAttributeWeak(index) && !(alteredAttributeValues[index] instanceof SoftReference)) {
|
||
|
|
currentAttributeValues[index] = new SoftReference(alteredAttributeValues[index]);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
currentAttributeValues[index] = alteredAttributeValues[index];
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
if(hasChanged) {
|
||
|
|
decrementVirtualObjectChangeCounter();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
hasChanged = false;
|
||
|
|
canUpdate = false;
|
||
|
|
resetAlteredFlag();
|
||
|
|
}//resetObjectChangeFlags()//
|
||
|
|
/**
|
||
|
|
* Resets all the change flags for this object and all part-of objects.
|
||
|
|
*/
|
||
|
|
public void resetVirtualObjectChangeFlags() {
|
||
|
|
resetObjectChangeFlags();
|
||
|
|
resetAlteredFlag();
|
||
|
|
|
||
|
|
//Search all part-of references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
if((getIsAttributePartOf(index)) && (value instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
if(handler != null) {
|
||
|
|
handler.resetVirtualObjectChangeFlags(value);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//resetVirtualObjectChangeFlags()//
|
||
|
|
/**
|
||
|
|
* Sets the value associated with the specified attribute.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param value The value that should be associated with this attribute.
|
||
|
|
* @param changeContext The context under which this attribute is being changed.
|
||
|
|
* @return Whether the value was changed. This will only be false if the new value and old value are identical and no change was required.
|
||
|
|
*/
|
||
|
|
protected boolean setAttributeValue(int attributeNumber, Object value, int changeContext) {
|
||
|
|
return setAttributeValue(attributeNumber, value, changeContext, true);
|
||
|
|
}//setAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Determines whether the change is an untrusted change.
|
||
|
|
* @param changeContext The change's flags.
|
||
|
|
* @return Whether the change is considered to be untrusted.
|
||
|
|
* @see #CONTEXT_UNTRUSTED
|
||
|
|
*/
|
||
|
|
public static boolean isUntrustedChangeContext(int changeContext) {
|
||
|
|
return (changeContext & CONTEXT_TRUSTED) == 0;
|
||
|
|
}//isUntrustedChangeContext()//
|
||
|
|
/**
|
||
|
|
* Determines whether the change is a trusted change.
|
||
|
|
* @param changeContext The change's flags.
|
||
|
|
* @return Whether the change is considered to be trusted.
|
||
|
|
* @see #CONTEXT_TRUSTED
|
||
|
|
*/
|
||
|
|
public static boolean isTrustedChangeContext(int changeContext) {
|
||
|
|
return (changeContext & CONTEXT_TRUSTED) != 0;
|
||
|
|
}//isTrustedChangeContext()//
|
||
|
|
/**
|
||
|
|
* Determines whether the change is a reflection update change.
|
||
|
|
* @param changeContext The change's flags.
|
||
|
|
* @return Whether the change is an update from a reflected object.
|
||
|
|
* @see #CONTEXT_UPDATE
|
||
|
|
*/
|
||
|
|
public static boolean isUpdateChangeContext(int changeContext) {
|
||
|
|
return (changeContext & CONTEXT_UPDATE) != 0;
|
||
|
|
}//isUpdateChangeContext()//
|
||
|
|
/**
|
||
|
|
* Sets the value associated with the specified attribute.
|
||
|
|
* <p>Warning: Since there is no synchronization, there is no guarentee that the value will actually be updated until a synch block is exited by a modifying thread and entered by this thread.</p>
|
||
|
|
* @param attributeNumber The unique (for this type hierarchy) number that identifies the attribute.
|
||
|
|
* @param value The value that should be associated with this attribute.
|
||
|
|
* @param changeContext The context under which this attribute is being changed.
|
||
|
|
* @param updateReflections Whether the reflections should be updated. If this is false then the caller is responsible for that action.
|
||
|
|
* @return Whether the value was changed. This will only be false if the new value and old value are identical and no change was required.
|
||
|
|
*/
|
||
|
|
private boolean setAttributeValue(int attributeNumber, Object value, int changeContext, boolean updateReflections) {
|
||
|
|
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(attributeNumber);
|
||
|
|
boolean isBound = attributeMetadata.isBackReference() || attributeMetadata.isAttributeBound();
|
||
|
|
Object oldValue = null;
|
||
|
|
Object newValue = value == null ? NULL_VALUE : value;
|
||
|
|
boolean result = true;
|
||
|
|
|
||
|
|
//Perform some lock checking to provide debug output on possible threading errors.//
|
||
|
|
if(isCheckingLocks && !isReflection() && getIsObjectNew() && !getSupportedObject().getMonitor().isLockingThread()) {
|
||
|
|
Debug.log(new RuntimeException("Possible thread lock error: Detected access of a shared object's attribute without holding the object's lock."));
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if((isTrustedChangeContext(changeContext)) && (alteredAttributeValues[attributeNumber] != null)) {
|
||
|
|
oldValue = alteredAttributeValues[attributeNumber];
|
||
|
|
//Change the attribute value even if the old value and new value are the same, since the altered attribute value will need to be cleared and event fired.//
|
||
|
|
internalSetAttributeValue(attributeNumber, newValue, oldValue, changeContext, updateReflections);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
oldValue = internalGetAttributeValue(attributeNumber);
|
||
|
|
|
||
|
|
//Handle lazy loaded attribute or those needing lazy loading.//
|
||
|
|
if((!isBound) && (!isTrustedChangeContext(changeContext))) {
|
||
|
|
//If the value is loading then wait, otherwise lazy initialize the value if it can be initialized. This is necessary so that the change flags are correct.//
|
||
|
|
if(oldValue == VALUE_LOADING) {
|
||
|
|
suspend();
|
||
|
|
|
||
|
|
try {
|
||
|
|
while((newValue = internalGetAttributeValue(attributeNumber)) == VALUE_LOADING) {
|
||
|
|
synchronized(this) {
|
||
|
|
wait(1000);
|
||
|
|
}//synchronized//
|
||
|
|
}//while//
|
||
|
|
}//try//
|
||
|
|
catch(InterruptedException e) {
|
||
|
|
Debug.handle(e);
|
||
|
|
}//catch//
|
||
|
|
|
||
|
|
resume();
|
||
|
|
}//if//
|
||
|
|
else if((oldValue == null) && (isReflection()) && (attributeMetadata.isReflectable())) {
|
||
|
|
oldValue = loadReflectedAttribute(attributeNumber, attributeMetadata);
|
||
|
|
|
||
|
|
if(oldValue == null) {
|
||
|
|
oldValue = NULL_VALUE;
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else if((oldValue == null) && (attributeMetadata.isLazy())) {
|
||
|
|
oldValue = lazyLoadAttribute(attributeNumber, attributeMetadata);
|
||
|
|
|
||
|
|
if(oldValue == null) {
|
||
|
|
oldValue = NULL_VALUE;
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If the old value and new value are not the same then change the value.//
|
||
|
|
if(!compareAttributeValues(newValue, oldValue)) {
|
||
|
|
internalSetAttributeValue(attributeNumber, newValue, oldValue, changeContext, updateReflections);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
result = false;
|
||
|
|
}//else//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//setAttributeValue()//
|
||
|
|
/**
|
||
|
|
* Sets the repository identifier for the repository that the entity was read from.
|
||
|
|
* @param repositoryIdentifier The entity's associated repository identifier, or null if the entity was not previously read from the repository.
|
||
|
|
*/
|
||
|
|
public void setRepositoryIdentifier(Object repositoryIdentifier) {
|
||
|
|
this.repositoryIdentifier = repositoryIdentifier;
|
||
|
|
this.typeRepositoryMetadata = repositoryIdentifier != null ? typeMetadata.getTypeRepositoryMetadata(repositoryIdentifier) : null;
|
||
|
|
}//setRepositoryIdentifier()//
|
||
|
|
/**
|
||
|
|
* Sets the object this attribute support is supporting.
|
||
|
|
* @param supportedObject The object being supported by this object.
|
||
|
|
*/
|
||
|
|
public void setSupportedObject(IEntity supportedObject) {
|
||
|
|
this.supportedObject = supportedObject;
|
||
|
|
|
||
|
|
super.setSupportedObject((IReflectableObject) supportedObject);
|
||
|
|
}//setSupportedObject()//
|
||
|
|
/**
|
||
|
|
* Collects the IReflectable instances that should be reflected and returned to the synchronizing reflection context, upon completing a synchronization.
|
||
|
|
* @param createReflectDataContext The context used to add IReflectable objects for later reflecting.
|
||
|
|
*/
|
||
|
|
public void collectPostSynchronizeReflectables(CreateReflectDataContext createReflectDataContext) {
|
||
|
|
//Search all attributes for IReflectable instances that are part-of this object.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
//If the attribute is IReflectable and is part-of this object then include it.//
|
||
|
|
if(value instanceof IReflectable && getIsAttributePartOf(index)) {
|
||
|
|
//Include the found IReflectable object.//
|
||
|
|
createReflectDataContext.include((IReflectable) value);
|
||
|
|
//Recursively search for IReflectable objects to include.//
|
||
|
|
((IReflectable) value).zzrCollectPostSynchronizeReflectables(value, createReflectDataContext);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//collectPostSynchronizeReflectables()//
|
||
|
|
/**
|
||
|
|
* Gets a string representing this object.
|
||
|
|
* The returned string is for debugging purposes only.
|
||
|
|
* @return A debug string showing the object and it's values.
|
||
|
|
*/
|
||
|
|
public String toString() {
|
||
|
|
String result = super.toString();
|
||
|
|
|
||
|
|
try {
|
||
|
|
result += "\r\n\tSupporting an instance of " + getSupportedObject().getClass().getName();
|
||
|
|
result += "\r\n\t[Can Store in Repository] = " + getIsObjectRepositoryBound();
|
||
|
|
result += "\r\n\t[Stored in Repository] = " + (repositoryIdentifier != null);
|
||
|
|
result += "\r\n\t[Has Changed] = " + hasChanged;
|
||
|
|
result += "\r\n\t[Can Update] = " + canUpdate;
|
||
|
|
result += "\r\n\t[Virtual Object Change Counter] = " + virtualObjectChangeCounter;
|
||
|
|
|
||
|
|
if(typeMetadata != null) {
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
if(value != null && !(value instanceof NullValue) && value != VALUE_LOADING) {
|
||
|
|
value = getAttributeValue(index);
|
||
|
|
}//if//
|
||
|
|
else if(currentAttributeValues[index] == VALUE_LOADING) {
|
||
|
|
value = "<value loading>";
|
||
|
|
}//else if//
|
||
|
|
else if(value instanceof NullValue) {
|
||
|
|
value = "null";
|
||
|
|
}//else if//
|
||
|
|
else if(getIsAttributeCalculated(index)) {
|
||
|
|
value = "<calculated value not set>";
|
||
|
|
}//else if//
|
||
|
|
else if(getIsAttributeLazy(index)) {
|
||
|
|
if(getIsAttributeWeak(index)) {
|
||
|
|
value = "<weak & lazy value not set>";
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
value = "<lazy value not set>";
|
||
|
|
}//else//
|
||
|
|
}//else if//
|
||
|
|
else if(getIsAttributeWeak(index)) {
|
||
|
|
value = "<weak value not set>";
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
value = "<value not set>";
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
result += "\r\n\t" + getAttributeName(index) + " = " + (value instanceof com.foundation.common.IEntity ? value.getClass().getName() + '@' + Integer.toHexString(value.hashCode()) : value != null ? value.toString() : "null");
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//try//
|
||
|
|
catch(Throwable e) {
|
||
|
|
Debug.log(e);
|
||
|
|
}//catch//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//toString()//
|
||
|
|
/**
|
||
|
|
* Generates a string representing the current state of all the calculated attribute listeners for the object.
|
||
|
|
* @param handler The optional handler used to convert Entity instances into strings.
|
||
|
|
* @return The string containing the current state of all calculated attribute listeners.
|
||
|
|
*/
|
||
|
|
public String calculatedAttributeListenersToString(ModelListener.DebugHandler handler) {
|
||
|
|
StringBuffer buffer = new StringBuffer(1000);
|
||
|
|
int listenerIndex = 0;
|
||
|
|
|
||
|
|
for(int attributeNumber = 0; attributeNumber < currentAttributeValues.length; attributeNumber++) {
|
||
|
|
if(getIsCalculated(attributeNumber)) {
|
||
|
|
ModelListener listener = (ModelListener) calculatedAttributeListeners[listenerIndex];
|
||
|
|
|
||
|
|
buffer.append("ModelListener structure for: ");
|
||
|
|
buffer.append(getSupportedObject().getClass().getName());
|
||
|
|
buffer.append('.');
|
||
|
|
buffer.append(getAttributeName(attributeNumber));
|
||
|
|
buffer.append("\r\n");
|
||
|
|
listener.debug(buffer, handler, 1);
|
||
|
|
listenerIndex++;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return buffer.toString();
|
||
|
|
}//calculatedAttributeListenersToString()//
|
||
|
|
/**
|
||
|
|
* Undoes all changes made to the object since the change flags were last reset.
|
||
|
|
* <p>Note that change flags are automatically reset after applying the changes to a repository and commiting.</p>
|
||
|
|
*/
|
||
|
|
public void undoObjectChanges() {
|
||
|
|
//Reset the altered values.//
|
||
|
|
for(int index = alteredAttributeValues.length - 1; index >= 0; index--) {
|
||
|
|
if(alteredAttributeValues[index] != null) {
|
||
|
|
Object oldValue = alteredAttributeValues[index];
|
||
|
|
|
||
|
|
alteredAttributeValues[index] = null;
|
||
|
|
fireUpdates(index, oldValue, internalGetAttributeValue(index), true, 0);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
if(hasChanged) {
|
||
|
|
decrementVirtualObjectChangeCounter();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
hasChanged = false;
|
||
|
|
canUpdate = false;
|
||
|
|
}//undoObjectChanges()//
|
||
|
|
/**
|
||
|
|
* Undoes all changes made to the object since the change flags were last reset.
|
||
|
|
* <p>Note that change flags are automatically reset after applying the changes to a repository and commiting.</p>
|
||
|
|
*/
|
||
|
|
public void undoVirtualObjectChanges() {
|
||
|
|
//TODO: Are we sure we want to undo the changes before navigating the tree? or after? or both?
|
||
|
|
undoObjectChanges();
|
||
|
|
|
||
|
|
//Search all part-of references.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
Object value = internalGetAttributeValue(index);
|
||
|
|
|
||
|
|
if((getIsAttributePartOf(index)) && (value instanceof ISupportsContainment)) {
|
||
|
|
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler();
|
||
|
|
|
||
|
|
if(handler != null) {
|
||
|
|
handler.reverseVirtualObjectChanges(value);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//undoVirtualObjectChanges()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(com.common.io.IObjectOutputStream out) throws java.io.IOException {
|
||
|
|
out.writeByte(2);
|
||
|
|
|
||
|
|
//Write the attributes, sending the attribute name, the value, and whether it was de-proxied.//
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
if(!getIsAttributeTransient(index)) {
|
||
|
|
Object value = getAttributeValue(index);
|
||
|
|
boolean deproxy = false;
|
||
|
|
|
||
|
|
//If the value is a reflection of another value, then we really want to serialize the reflected value reference, and not the reflection.//
|
||
|
|
if((value instanceof IReflectable) && (((IReflectable) value).isReflection())) {
|
||
|
|
value = ((IReflectable) value).getReflected();
|
||
|
|
deproxy = true;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
out.writeObject(getAttributeName(index));
|
||
|
|
|
||
|
|
if(value instanceof ISupportsContainment) {
|
||
|
|
lock(value);
|
||
|
|
|
||
|
|
try {
|
||
|
|
out.writeObject(value);
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
unlock(value);
|
||
|
|
}//finally//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
out.writeObject(value);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
out.writeBoolean(deproxy); //Flag the value so that the reader can get the proxied value if it is local.//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Write an ending null string to indicate that the list of attributes is complete.//
|
||
|
|
out.writeObject(null);
|
||
|
|
out.writeInt(getModelMappingNumber());
|
||
|
|
out.writeInt(getHash());
|
||
|
|
}//writeExternal()//
|
||
|
|
/**
|
||
|
|
* Writes the object to a stream.
|
||
|
|
* @param out The stream to write to.
|
||
|
|
*/
|
||
|
|
public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
|
||
|
|
out.writeByte(2);
|
||
|
|
|
||
|
|
for(int index = 0; index < currentAttributeValues.length; index++) {
|
||
|
|
if(!getIsAttributeTransient(index)) {
|
||
|
|
Object value = getAttributeValue(index);
|
||
|
|
boolean deproxy = false;
|
||
|
|
|
||
|
|
//If the value is a reflection of another value, then we really want to serialize the reflected value reference, and not the reflection.//
|
||
|
|
if((value instanceof IReflectable) && (((IReflectable) value).isReflection())) {
|
||
|
|
value = ((IReflectable) value).getReflected();
|
||
|
|
deproxy = true;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
out.writeObject(getAttributeName(index));
|
||
|
|
|
||
|
|
if(value instanceof ISupportsContainment) {
|
||
|
|
lock(value);
|
||
|
|
|
||
|
|
try {
|
||
|
|
out.writeObject(value);
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
unlock(value);
|
||
|
|
}//finally//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
out.writeObject(value);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
out.writeBoolean(deproxy); //Flag the value so that the reader can get the proxied value if it is local.//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Write an ending null string to indicate that the list of attributes is complete.//
|
||
|
|
out.writeObject(null);
|
||
|
|
out.writeInt(getModelMappingNumber());
|
||
|
|
}//writeExternal()//
|
||
|
|
/**
|
||
|
|
* Obtains a lock on the given object.
|
||
|
|
* <p><b>Warning: If the object reference does not implement ISupportsContainment (or is null) then the global monitor will be used for the lock.</b></p>
|
||
|
|
* <p><b>Warning: It is possible that the thread's lock on this object's monitor will be suspended if a deadlock situation is detected. The lock will be re-obtained when the passed object is unlocked.</b></p>
|
||
|
|
* <p><b>Warning: The caller must call this inside a try block and must call unlock inside the finally part of the try block. The caller should not lock more than one object at a time.</b></p>
|
||
|
|
* @param object The object to be locked.
|
||
|
|
*/
|
||
|
|
public void lock(Object object) {
|
||
|
|
Monitor objectMonitor = null;
|
||
|
|
|
||
|
|
if(object instanceof Monitor) {
|
||
|
|
objectMonitor = (Monitor) object;
|
||
|
|
}//if//
|
||
|
|
else if(object instanceof ISupportsContainment) {
|
||
|
|
objectMonitor = ((ISupportsContainment) object).getMonitor();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
lock(objectMonitor);
|
||
|
|
}//lock()//
|
||
|
|
/**
|
||
|
|
* Releases the lock on the given object.
|
||
|
|
* If a previous lock was held and suspended when the given object was locked then that lock will be resumed automatically.
|
||
|
|
* @param object The object to be unlocked.
|
||
|
|
*/
|
||
|
|
public void unlock(Object object) {
|
||
|
|
Monitor objectMonitor = null;
|
||
|
|
|
||
|
|
if(object instanceof Monitor) {
|
||
|
|
objectMonitor = (Monitor) object;
|
||
|
|
}//if//
|
||
|
|
else if(object instanceof ISupportsContainment) {
|
||
|
|
objectMonitor = ((ISupportsContainment) object).getMonitor();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
unlock(objectMonitor);
|
||
|
|
}//unlock()//
|
||
|
|
/**
|
||
|
|
* Blocks the thread indefinately, or until notify is called.
|
||
|
|
* <p><b>Warning: This method actually calls wait on the calling thread's Thread object. If used, the application may not use thread objects for calling wait/notify/notifyAll otherwise unexpected results will ensue.</b></p>
|
||
|
|
* @param object The object whose monitor will be waited on (the calling thread must already have locked on this monitor).
|
||
|
|
*/
|
||
|
|
public void wait(Object object) {
|
||
|
|
wait(object, 0);
|
||
|
|
}//wait()//
|
||
|
|
/**
|
||
|
|
* Blocks the thread for the given time period, or until notify is called.
|
||
|
|
* <p><b>Warning: This method actually calls wait on the calling thread's Thread object. If used, the application may not use thread objects for calling wait/notify/notifyAll otherwise unexpected results will ensue.</b></p>
|
||
|
|
* @param object The object whose monitor will be waited on (the calling thread must already have locked on this monitor).
|
||
|
|
* @param timeout The count of milliseconds before the wait ends, or zero if it will wait indefinately.
|
||
|
|
*/
|
||
|
|
public void wait(Object object, long timeout) {
|
||
|
|
Monitor objectMonitor = null;
|
||
|
|
|
||
|
|
if(object instanceof Monitor) {
|
||
|
|
objectMonitor = (Monitor) object;
|
||
|
|
}//if//
|
||
|
|
else if(object instanceof ISupportsContainment) {
|
||
|
|
objectMonitor = ((ISupportsContainment) object).getMonitor();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
wait(objectMonitor, timeout);
|
||
|
|
}//wait()//
|
||
|
|
/**
|
||
|
|
* Notifies the longest blocked thread that it can run again.
|
||
|
|
* <p><b>Warning: This method actually calls wait on the calling thread's Thread object. If used, the application may not use thread objects for calling wait/notify/notifyAll otherwise unexpected results will ensue.</b></p>
|
||
|
|
* @param object The object whose monitor the waiting thread to be unblocked will have waited on.
|
||
|
|
*/
|
||
|
|
public void notify(Object object) {
|
||
|
|
Monitor objectMonitor = null;
|
||
|
|
|
||
|
|
if(object instanceof Monitor) {
|
||
|
|
objectMonitor = (Monitor) object;
|
||
|
|
}//if//
|
||
|
|
else if(object instanceof ISupportsContainment) {
|
||
|
|
objectMonitor = ((ISupportsContainment) object).getMonitor();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
notify(objectMonitor);
|
||
|
|
}//notify()//
|
||
|
|
/**
|
||
|
|
* Notifies the longest blocked thread that it can run again.
|
||
|
|
* <p><b>Warning: This method actually calls wait on the calling thread's Thread object. If used, the application may not use thread objects for calling wait/notify/notifyAll otherwise unexpected results will ensue.</b></p>
|
||
|
|
* @param object The object whose monitor the waiting thread to be unblocked will have waited on.
|
||
|
|
*/
|
||
|
|
public void notifyAll(Object object) {
|
||
|
|
Monitor objectMonitor = null;
|
||
|
|
|
||
|
|
if(object instanceof Monitor) {
|
||
|
|
objectMonitor = (Monitor) object;
|
||
|
|
}//if//
|
||
|
|
else if(object instanceof ISupportsContainment) {
|
||
|
|
objectMonitor = ((ISupportsContainment) object).getMonitor();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
notifyAll(objectMonitor);
|
||
|
|
}//notifyAll()//
|
||
|
|
}//AttributeSupport//
|