Files
Brainstorm/Foundation/src/com/foundation/util/HashSetAdapter.java

856 lines
34 KiB
Java
Raw Normal View History

2014-05-30 10:31:51 -07:00
/*
* Copyright (c) 2005,2009 Declarative Engineering LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Declarative Engineering LLC
* verson 1 which accompanies this distribution, and is available at
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
*/
package com.foundation.util;
import com.common.debug.*;
import com.common.exception.*;
import com.common.thread.Monitor;
import com.common.thread.ThreadService;
import com.common.util.ICollection;
import com.common.util.IIterator;
import com.common.util.LiteHashSet;
import com.common.util.LiteList;
import com.common.comparison.*;
import com.foundation.attribute.AttributeSupport;
import com.foundation.common.BackReferenceNode;
import com.foundation.common.IEntity;
import com.foundation.common.MetadataContainer;
import com.foundation.metadata.CloneContext;
import com.foundation.metadata.ISupportsContainment;
import com.foundation.metadata.LoadAttributesContext;
import com.foundation.metadata.MetadataService;
import com.foundation.metadata.TypeCallbackHandler;
import com.foundation.util.ListAdapter.Converter;
import com.foundation.util.ListAdapter.IFilter;
import com.foundation.util.Updater.UpdaterRunnable;
/**
* <p><b>Warning: The creating thread must have synchronous access to the parent (must synchronize on the parent's getMonitor() object, or must have exclusive access to the parent collection).<b></p>
* <p>TODO: Add the ability to disconnect the map.</p>
* <p>TODO: Add serialization code and test.</p>
*/
public class HashSetAdapter extends LiteHashSet implements ISupportsContainment, IInlineCollectionObservable {
protected static final int CHANGE_TYPE_ADD = 0;
protected static final int CHANGE_TYPE_REMOVE = 1;
protected static final int CHANGE_TYPE_REMOVE_ALL = 2;
protected static final int CHANGE_TYPE_START_CHANGES = 3;
protected static final int CHANGE_TYPE_STOP_CHANGES = 4;
/** An identifier used by the collection observer. */
private static final Integer ADD = new Integer(0);
/** An identifier used by the collection observer. */
private static final Integer REMOVE = new Integer(1);
/** An identifier used by the collection observer. */
private static final Integer REMOVE_ALL = new Integer(2);
protected static final TypeCallbackHandler TYPE_CALLBACK_HANDLER = new TypeCallbackHandler() {
public Monitor getMonitor(Object instance) {
return ((ManagedCollection) instance).getMonitor();
}//getMonitor()//
public void setMonitor(Object instance, IEntity parent, boolean isPartOf) {
if(parent == null) {
if(((HashSetAdapter) instance).getPartOfEntityCounter() != 0) {
((HashSetAdapter) instance).setPartOfEntityCounter(((HashSetAdapter) instance).getPartOfEntityCounter() - 1);
if(((HashSetAdapter) instance).getPartOfEntityCounter() == 0) {
((HashSetAdapter) instance).setPartOfEntity(null);
((HashSetAdapter) instance).isPartOf(false);
}//if//
}//if//
}//if//
else if(((HashSetAdapter) instance).getPartOfEntity() == parent) {
((HashSetAdapter) instance).setPartOfEntityCounter(((HashSetAdapter) instance).getPartOfEntityCounter() + 1);
}//else if//
else if(((HashSetAdapter) instance).getPartOfEntity() != null) {
if(((HashSetAdapter) instance).getMonitor() != parent.getMonitor()) {
Debug.log(new RuntimeException("Error: Invalid monitor state: A managed hash map cannot be contained by more than one object or collection."));
//TODO: Throw a custom exception and catch it in the code that is setting up the containment.//
}//if//
}//else if//
else {
((HashSetAdapter) instance).setPartOfEntityCounter(1);
((HashSetAdapter) instance).setPartOfEntity(parent);
((HashSetAdapter) instance).isPartOf(isPartOf);
}//else//
}//setMonitor()//
public AttributeSupport getAttributeSupport(Object instance) {
return null;
}//getAttributeSupport()//
public void preClone(Object instance, MetadataContainer metadata, CloneContext cloneContext) {
//Does nothing.//
}//preClone()//
public ISupportsContainment clone(Object instance, MetadataContainer metadata, CloneContext cloneContext) {
//TODO: Implement the clone functionality?
return null;
}//clone()//
public void loadAttributes(Object instance, LoadAttributesContext context) {
//Does nothing since it is dependant on another collection.//
}//loadAttributes()//
public void postLoadAttributes(Object instance, LoadAttributesContext context) {
//Does nothing since it is dependant on another collection.//
}//postLoadAttributes()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#getIsVirtualObjectChanged(java.lang.Object)
*/
public boolean getIsVirtualObjectChanged(Object instance) {
//Always false since the adapter is dependant on another collection and cannot be modified except through the dependant collection.//
return false;
}//getIsVirtualObjectChanged()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#resetVirtualObjectChangeFlags(java.lang.Object)
*/
public void resetVirtualObjectChangeFlags(Object instance) {
//Does nothing since it is dependant on another collection.//
}//resetVirtualObjectChangeFlags()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#reverseVirtualObjectChanges(java.lang.Object)
*/
public void reverseVirtualObjectChanges(Object instance) {
//Does nothing since it is dependant on another collection.//
}//reverseVirtualObjectChanges()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#getIsVirtualObjectAltered(java.lang.Object)
*/
public boolean getIsVirtualObjectAltered(Object instance) {
//Always false since the adapter is dependant on another collection and cannot be modified except through the dependant collection.//
return false;
}//getIsVirtualObjectAltered()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#resetVirtualObjectAlteredFlags(java.lang.Object)
*/
public void resetVirtualObjectAlteredFlags(Object instance) {
//Does nothing.//
}//resetVirtualObjectAlteredFlags()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#setMaintainWeakLinkBackReferences(java.lang.Object)
*/
public void setMaintainWeakLinkBackReferences(Object instance) {
((HashSetAdapter) instance).setMaintainWeakLinkBackReferences();
}//setMaintainWeakLinkBackReferences()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#addWeakLinkBackReference(java.lang.Object, java.lang.Object)
*/
public void addWeakLinkBackReference(Object instance, Object collection) {
((HashSetAdapter) instance).weakLinkBackReferenceRootNode = new BackReferenceNode(collection, ((HashSetAdapter) instance).weakLinkBackReferenceRootNode);
}//addWeakLinkBackReference()//
/* (non-Javadoc)
* @see com.foundation.metadata.TypeCallbackHandler#removeWeakLinkBackReference(java.lang.Object, java.lang.Object)
*/
public void removeWeakLinkBackReference(Object instance, Object collection) {
BackReferenceNode previous = null;
BackReferenceNode next = ((HashSetAdapter) instance).weakLinkBackReferenceRootNode;
while(next != null && next.getReference() != collection) {
previous = next;
next = next.getNext();
}//while//
if(next != null) {
if(previous != null) {
previous.setNext(next.getNext());
}//if//
else {
((HashSetAdapter) instance).weakLinkBackReferenceRootNode = next.getNext();
}//else//
}//if//
}//removeWeakLinkBackReference()//
};//TypeCallbackHandler//
static {
MetadataService.getSingleton().setTypeCallbackHandler(HashSetAdapter.class, TYPE_CALLBACK_HANDLER);
}//static//
/**
* Defines a collection operation that may make multiple changes to the collection.
* The collection operation makes making changes more efficient since only one event is fired for a set of changes.
*/
public interface ICollectionOperation {
public void run(HashSetAdapter collection);
}//ICollectionOperation//
/** A collection of ICollectionObserver instances which will be provided immediate inline change notification. */
private LiteList collectionObservers = null;
/** The number of times the part of entity has been set to the same value. */
private volatile int partOfEntityCounter = 0;
/** A reference to the entity that this collection is a part of. If the collection is not a part of an entity then this will be null. */
private volatile IEntity partOfEntity = null;
/** The monitor for this collection or null if this map object is to be used as the monitor. Users will synchronize on this monitor prior to accessing the public methods. */
private volatile Monitor entityMonitor = null;
/** TODO: It would be nice if this hashmap had the monitor built in instead of creating a separate object. This will require changing the hierarchy which is probably best anyways to allow for reflectable hashmaps and advanced behavior. */
// private final Monitor localMonitor = new Monitor(this);
/** Whether this collection is part of another object (logically an integrated component and cannot exist outside that context). */
private boolean isPartOf = false;
/** 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 boolean isAltered = false;
/** Whether back references should be maintained between collected items and the collection to prevent the model referencing this collection from GC'ing the collection while collected items are referenced. */
private boolean maintainWeakLinkBackReferences = false;
/** The root node in the linked list of back references used when the object is referenced by a collection that is referenced by an entity using the part-of and weak flags. */
private BackReferenceNode weakLinkBackReferenceRootNode = null;
/** The observer used to listen to changes in the parent. */
private CollectionObserver observer = new CollectionObserver();
/** The filter used to determine what is added to the adapter. */
private transient IFilter filter = null;
/** The converter used to convert values added to the adapter. */
private transient Converter converter = null;
/**
* Observes to the attached collection for changes.
*/
private class CollectionObserver implements IInlineCollectionObserver {
/** The changed queued up, interleved with identifiers for ADD, REMOVE, and REMOVE_ALL. */
private LiteList queuedChanges = null;
private class CollectionObserverUpdaterRunnable extends UpdaterRunnable {
private Object value;
private Integer command;
private Monitor monitor;
public CollectionObserverUpdaterRunnable(Monitor monitor, Integer command, Object value) {
this.command = command;
this.value = value;
this.monitor = monitor;
}//CollectionObserverUpdaterRunnable()//
public Monitor getMonitor() {
return monitor;
}//getMonitor()//
public void run() {
internalProcessCommand(command, value);
}//run()//
}//CollectionObserverUpdaterRunnable//
public CollectionObserver() {
}//CollectionObserver()//
public void valueAdded(final Object value) {
if((filter == null) || (filter.include(value))) {
if(queuedChanges != null) {
queuedChanges.add(ADD);
queuedChanges.add(value);
}//if//
else {
//Thread the changes if we are not in a single threaded context (in which case we will presume to be running on the context's thread.//
if(getMonitor() != null && !ThreadService.getIsSingleThreadedContext()) {
Updater.getSingleton().run(new CollectionObserverUpdaterRunnable(getMonitor(), ADD, value));
}//if//
else {
internalValueAdded(value);
}//else//
}//else//
}//if//
}//valueAdded()//
public void valueRemoved(final Object value) {
if((filter == null) || (filter.include(value))) {
if(queuedChanges != null) {
queuedChanges.add(REMOVE);
queuedChanges.add(value);
}//if//
else {
//Thread the changes if we are not in a single threaded context (in which case we will presume to be running on the context's thread.//
if(getMonitor() != null && !ThreadService.getIsSingleThreadedContext()) {
Updater.getSingleton().run(new CollectionObserverUpdaterRunnable(getMonitor(), REMOVE, value));
}//if//
else {
internalValueRemoved(value);
}//else//
}//else//
}//if//
}//valueRemoved()//
public void removingAll() {
if(queuedChanges != null) {
queuedChanges.add(REMOVE_ALL);
queuedChanges.add(null);
}//if//
else {
//Thread the changes if we are not in a single threaded context (in which case we will presume to be running on the context's thread.//
if(getMonitor() != null && !ThreadService.getIsSingleThreadedContext()) {
Updater.getSingleton().run(new CollectionObserverUpdaterRunnable(getMonitor(), REMOVE_ALL, null));
}//if//
else {
internalRemovingAll();
}//else//
}//else//
}//removingAll()//
public void startChanges(int changeCount) {
queuedChanges = new LiteList(changeCount > 0 ? changeCount : 100, 1000);
}//startChanges()//
public void stopChanges() {
//Thread the changes if we are not in a single threaded context (in which case we will presume to be running on the context's thread.//
if(getMonitor() != null && !ThreadService.getIsSingleThreadedContext()) {
Updater.getSingleton().run(new UpdaterRunnable() {
public void run() {
internalStopChanges();
}//run()//
public Monitor getMonitor() {
return HashSetAdapter.this.getMonitor();
}//getMonitor()//
});
}//if//
else {
if(queuedChanges != null && queuedChanges.getSize() > 0) {
internalStopChanges();
}//if//
}//else//
}//stopChanges()//
/**
* Proces the queued changes in batch.
*/
private void internalStopChanges() {
if(collectionObservers != null) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).startChanges(queuedChanges.getSize() >> 1);
}//for//
}//if//
for(int index = 0; index < queuedChanges.getSize(); index ++) {
internalProcessCommand((Integer) queuedChanges.get(index++), queuedChanges.get(index));
}//for//
if(collectionObservers != null) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).stopChanges();
}//for//
}//if//
queuedChanges.removeAll();
queuedChanges = null;
}//internalStopChanges()//
private void internalProcessCommand(Integer command, Object value) {
switch(command.intValue()) {
case 0: { //ADD
internalValueAdded(value);
break;
}//case//
case 1: { //REMOVE
internalValueRemoved(value);
break;
}//case//
case 2: { //REMOVING ALL
internalRemovingAll();
break;
}//case//
}//switch//
}//internalProcessCommand()//
private void internalValueAdded(Object value) {
Object newValue = value;
if(converter != null) {
newValue = converter.internalAdd(newValue);
}//if//
internalAdd(newValue);
if(collectionObservers != null) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).valueAdded(value);
}//for//
}//if//
}//internalValueAdded()//
private void internalValueRemoved(Object value) {
Object oldValue = value;
if(converter != null) {
oldValue = converter.internalRemove(oldValue);
}//if//
internalRemove(oldValue);
if(collectionObservers != null) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).valueRemoved(value);
}//for//
}//if//
}//internalValueRemoved()//
private void internalRemovingAll() {
if(collectionObservers != null) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).removingAll();
}//for//
}//if//
if(converter != null) {
converter.getConversionMap().removeAll();
}//if//
internalRemoveAll();
}//internalRemovingAll()//
}//CollectionObserver//
/**
* HashSetAdapter constructor.
* <p><b>Warning: The creating thread must have synchronous access to the parent (must synchronize on the parent's getMonitor() object, or must have exclusive access to the parent collection).<b></p>
*/
protected HashSetAdapter() {
super();
}//HashSetAdapter()//
/**
* HashSetAdapter constructor.
* @param parent The map's parent source of collection values.
* @throws IllegalArgumentException If the initial capacity or load factor are not within their valid ranges.
*/
public HashSetAdapter(IInlineCollectionObservable parent) {
initialize(parent, DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_COMPARATOR, DEFAULT_STYLE);
}//HashSetAdapter()//
/**
* HashSetAdapter constructor.
* @param parent The map's parent source of collection values.
* @param initialCapacity Determines the size of the array holding the set values.
* @throws IllegalArgumentException If the initial capacity or load factor are not within their valid ranges.
*/
public HashSetAdapter(IInlineCollectionObservable parent, int initialCapacity) {
initialize(parent, initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_COMPARATOR, DEFAULT_STYLE);
}//HashSetAdapter()//
/**
* HashSetAdapter constructor.
* @param parent The map's parent source of collection values.
* @param initialCapacity Determines the size of the array holding the set values.
* @param filter A filter used to determine whether values should be included in this filtered collection.
* @param converter A converter used to convert the external value into the internal value.
* @throws IllegalArgumentException If the initial capacity or load factor are not within their valid ranges.
*/
public HashSetAdapter(IInlineCollectionObservable parent, int initialCapacity, IFilter filter, Converter converter) {
initialize(parent, initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_COMPARATOR, DEFAULT_STYLE);
this.filter = filter;
this.converter = converter;
}//HashSetAdapter()//
/**
* HashSetAdapter constructor.
* @param parent The map's parent source of collection values.
* @param initialCapacity Determines the size of the array holding the set values.
* @param loadFactor The ratio used to determine when to increase the size of the array holding the set values.
* @param locateComparator The comparator used by this collection for locating values. This may be null in which case the default comparator will be used (it uses logical equality for comparisons).
* @param allowMultipleInstanceReferences Whether multiple references to the same object instance can be stored in this collection. This is normally false.
* @throws IllegalArgumentException If the initial capacity or load factor are not within their valid ranges.
*/
public HashSetAdapter(IInlineCollectionObservable parent, int initialCapacity, float loadFactor, IComparator locateComparator, int style) {
initialize(parent, initialCapacity, loadFactor, locateComparator, style);
}//HashSetAdapter()//
/**
* HashSetAdapter constructor.
* @param parent The map's parent source of collection values.
* @param initialCapacity Determines the size of the array holding the set values.
* @param loadFactor The ratio used to determine when to increase the size of the array holding the set values.
* @param locateComparator The comparator used by this collection for locating values. This may be null in which case the default comparator will be used (it uses logical equality for comparisons).
* @param allowMultipleInstanceReferences Whether multiple references to the same object instance can be stored in this collection. This is normally false.
* @param filter A filter used to determine whether values should be included in this filtered collection.
* @param converter A converter used to convert the external value into the internal value.
* @throws IllegalArgumentException If the initial capacity or load factor are not within their valid ranges.
*/
public HashSetAdapter(IInlineCollectionObservable parent, int initialCapacity, float loadFactor, IComparator locateComparator, int style, IFilter filter, Converter converter) {
initialize(parent, initialCapacity, loadFactor, locateComparator, style);
this.filter = filter;
this.converter = converter;
}//HashSetAdapter()//
/**
* Initializes the read only map.
* <p><b>Warning: The creating thread must have synchronous access to the parent (must synchronize on the parent's getMonitor() object, or must have exclusive access to the parent collection).<b></p>
* @param parent The map's parent source of collection values.
* @param keyHandler The handler that will retreive each collection value's key.
*/
protected void initialize(IInlineCollectionObservable parent, int initialCapacity, float loadFactor, IComparator locateComparator, int style) {
initialize(initialCapacity, loadFactor, locateComparator, style);
//Add this object as a listener on the adaptable collection. The collection will notify us of all existing values.//
parent.addCollectionObserver(observer);
}//initialize()//
/**
* 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.
* </p>
*/
public boolean getIsAltered() {
return isAltered;
}//getIsAltered()//
/**
* 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.
* </p>
*/
public void resetAlteredFlag() {
if(isAltered) {
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(getPartOfEntity().getClass()).getTypeCallbackHandler();
handler.updateVirtualObjectAlteredCounter(getPartOfEntity(), false, 1);
}//if//
isAltered = false;
}//resetAlteredFlag()//
/* (non-Javadoc)
* @see com.common.util.LiteCollection#internalAdd(java.lang.Object)
*/
protected int internalAdd(Object value) {
HashSetEntry[] entries = getEntries();
//Ensure that null values are valid.//
if(value == null) {
value = NULL_VALUE;
}//if//
int hash = getLocateComparator().hash(value);
int index = (hash & 0x7FFFFFFF) % entries.length;
HashSetEntry entry = null;
if(getStyle() != STYLE_ADD_DUPLICATES) {
for(entry = entries[index]; entry != null; entry = entry.next) {
if((entry.hash == hash) && (Comparator.isEqual(getLocateComparator().compare(entry.value, value)))) {
if(getStyle() == STYLE_NO_DUPLICATES) {
return -1;
}//if//
else {
entry.count++;
return 0;
}//else//
}//if//
}//for//
}//if//
if(getSize() >= getThreshold()) {
rehash();
return add(value);
}//if//
entry = createEntry();
entry.hash = hash;
entry.value = value;
entry.next = entries[index];
entry.count = 1;
entries[index] = entry;
setSize(getSize() + 1);
if(value != null) {
setContainment(value, true);
}//if//
notifyCollectionObserversOnAdd(value);
return 0;
}//internalAdd()//
/* (non-Javadoc)
* @see com.common.util.LiteCollection#internalRemove(java.lang.Object)
*/
protected boolean internalRemove(Object value) {
HashSetEntry[] entries = getEntries();
//Ensure that null values are valid.//
if(value == null) {
value = NULL_VALUE;
}//if//
int hash = getLocateComparator().hash(value);
int index = (hash & 0x7FFFFFFF) % entries.length;
HashSetEntry entry = null;
HashSetEntry previous = null;
for(entry = entries[index], previous = null; entry != null; previous = entry, entry = entry.next) {
if((entry.hash == hash) && (Comparator.isEqual(getLocateComparator().compare(entry.value, value)))) {
entry.count--;
if(entry.count == 0) {
if(previous != null) {
previous.next = entry.next;
}//if//
else {
entries[index] = entry.next;
}//else//
setSize(getSize() - 1);
destroyEntry(entry);
if(value != null) {
setContainment(value, false);
}//if//
notifyCollectionObserversOnRemove(value);
}//if//
return true;
}//if//
}//for//
return false;
}//internalRemove()//
/* (non-Javadoc)
* @see com.common.util.LiteCollection#internalRemoveAll()
*/
protected void internalRemoveAll() {
LiteList removedData = new LiteList(iterator(), getSize(), 10, false);
notifyCollectionObserversOnRemovingAll();
super.internalRemoveAll();
if(removedData != null) {
//Clear containments.//
if(isPartOf) {
for(int index = 0; index < removedData.getSize(); index++) {
setContainment(removedData.get(index), false);
}//for//
}//if//
}//if//
}//internalRemoveAll()//
/* (non-Javadoc)
* @see com.common.util.LiteCollection#internalReplace(java.lang.Object, java.lang.Object)
*/
protected boolean internalReplace(Object oldValue, Object newValue) {
//Note: If this is ever supported, don't forget to replace null's with NULL_VALUE.//
throw new MethodNotSupportedException();
}//internalReplace()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObservable#getCollection()
*/
public ICollection getCollection() {
return new LiteList((IIterator) iterator(), this.getSize(), 10, true);
}//getCollection()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObservable#addCollectionObserver(com.foundation.util.IInlineCollectionObserver)
*/
public void addCollectionObserver(IInlineCollectionObserver observer) {
if(collectionObservers == null) {
collectionObservers = new LiteList(2, 4);
}//if//
collectionObservers.add(observer);
observer.startChanges(getSize());
//Pass all existing values to the observer.//
for(IIterator iterator = iterator(); iterator.hasNext();) {
observer.valueAdded(iterator.next());
}//for//
observer.stopChanges();
}//addCollectionObserver()//
/* (non-Javadoc)
* @see com.foundation.util.IInlineCollectionObservable#removeCollectionObserver(com.foundation.util.IInlineCollectionObserver)
*/
public void removeCollectionObserver(IInlineCollectionObserver observer) {
collectionObservers.remove(observer);
}//removeCollectionObserver()//
/**
* Gets the monitor used to synchronize on this entity.
* @return The monitor used by the entity if it is contained by another entity, or null if it is not contained by another entity.
*/
private Monitor getEntityMonitor() {
return entityMonitor;
}//getEntityMonitor()//
/**
* Sets the monitor used to synchronize on this entity.
* @param entityMonitor The monitor used by the entity if it is contained by another entity, or null if it is not contained by another entity.
*/
private void setEntityMonitor(Monitor entityMonitor) {
this.entityMonitor = entityMonitor;
}//setEntityMonitor()//
/**
* Gets whether the collection and its collected values are contained by another object.
* @return Whether the collected values are part of this collection's referencer.
*/
protected boolean isPartOf() {
return isPartOf;
}//isPartOf()//
/**
* 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.
*/
private int getPartOfEntityCounter() {
return partOfEntityCounter;
}//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.
*/
private void setPartOfEntityCounter(int partOfEntityCounter) {
this.partOfEntityCounter = partOfEntityCounter;
}//setPartOfEntityCounter()//
/**
* Gets the entity the collection is part of.
* @return The entity that this collection is a part of. This may be null.
*/
private IEntity getPartOfEntity() {
return partOfEntity;
}//getPartOfEntity()//
/**
* Sets the entity the collection is part of.
* @param partOfEntity The entity that this collection is a part of. This may be null.
*/
private void setPartOfEntity(IEntity partOfEntity) {
this.partOfEntity = partOfEntity;
setEntityMonitor(partOfEntity != null ? partOfEntity.getMonitor() : null);
}//setPartOfEntity()//
/**
* Sets whether the collection and its collected values are contained by another object.
* @param isPartOf Whether the collected values are part of this collection's referencer.
*/
private void isPartOf(boolean isPartOf) {
if(/*(!isReflection()) && */(this.isPartOf != isPartOf)) {
IIterator iterator = iterator();
this.isPartOf = isPartOf;
while(iterator.hasNext()) {
Object next = iterator.next();
if(next instanceof ISupportsContainment) {
MetadataService.getSingleton().getTypeMetadata(next.getClass()).getTypeCallbackHandler().setMonitor(next, isPartOf ? getPartOfEntity() : null, false);
}//if//
}//while//
}//if//
}//isPartOf()//
/* (non-Javadoc)
* @see com.foundation.metadata.ISupportsContainment#getMonitor()
*/
public Monitor getMonitor() {
Monitor monitor = getEntityMonitor();
if((monitor != null) && (monitor.getSupported() instanceof ISupportsContainment) && (monitor.getSupported() != this)) {
monitor = ((ISupportsContainment) monitor.getSupported()).getMonitor();
}//if//
return monitor;
}//getMonitor()//
/**
* Executes an operation on this collection such that events are condensed into one.
* @param operation the operation to be run.
*/
public void execute(ICollectionOperation operation) {
notifyCollectionObserversOnStart(0);
operation.run(this);
notifyCollectionObserversOnStop();
}//execute()//
/**
* Sets whether the value is part of the collection's referencer.
* @param value The collection value.
* @param isAdding Whether the list is adding the value to the collection.
*/
protected final void setContainment(Object value, boolean isAdding) {
if(/*(!isReflection()) && */(isPartOf) && (value != null) && (value instanceof ISupportsContainment)) {
MetadataService.getSingleton().getTypeMetadata(value.getClass()).getTypeCallbackHandler().setMonitor(value, isAdding ? getPartOfEntity() : null, false);
}//if//
}//setContainment()//
/* (non-Javadoc)
* @see com.foundation.clone.ICloneable#cloneObject(com.foundation.metadata.CloneContext, com.foundation.common.MetadataContainer)
*/
public Object cloneObject(CloneContext context, MetadataContainer metadata) {
return TYPE_CALLBACK_HANDLER.clone(this, metadata, context);
}//cloneObject()//
/* (non-Javadoc)
* @see java.lang.Object#clone()
*/
protected Object clone() {
return null;
}//clone()//
/**
* Loads attributes within the given context. The context controls which attributes are loaded from which objects.
* @param context The context for the load operation.
*/
protected void loadAttributes(LoadAttributesContext context) {
if(isPartOf()) {
IIterator iterator = iterator();
addCollectionObserver(context);
while(iterator.hasNext()) {
Object value = iterator.next();
if(value instanceof ISupportsContainment) {
context.include(value);
}//if//
}//while//
}//if//
}//loadAttributes()//
/**
* Called after the loadAttributes method and after the attributes have been used as needed.
* @param context The context for the load operation.
*/
protected void postLoadAttributes(LoadAttributesContext context) {
if(isPartOf()) {
removeCollectionObserver(context);
}//if//
}//postLoadAttributes()//
/**
* Determines whether there are any observers to this collection.
* @return Whether there are any observer objects attached to this collection.
*/
protected boolean hasCollectionObservers() {
return (collectionObservers != null) && (collectionObservers.getSize() > 0);
}//hasCollectionObservers()//
/**
* Notifies any collection listeners of a change in the collection values.
* @param value The value added.
*/
protected void notifyCollectionObserversOnAdd(Object value) {
if((collectionObservers != null) && (collectionObservers.getSize() > 0)) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).valueAdded(value);
}//for//
}//if//
}//notifyCollectionObserversOnAdd()//
/**
* Notifies any collection listeners of a change in the collection values.
* @param value The value removed.
*/
protected void notifyCollectionObserversOnRemove(Object value) {
if((collectionObservers != null) && (collectionObservers.getSize() > 0)) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).valueRemoved(value);
}//for//
}//if//
}//notifyCollectionObserversOnRemove()//
/**
* Notifies any collection listeners of a change in the collection values.
*/
protected void notifyCollectionObserversOnRemovingAll() {
if((collectionObservers != null) && (collectionObservers.getSize() > 0)) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).removingAll();
}//for//
}//if//
}//notifyCollectionObserversOnRemovingAll()//
/**
* Notifies any collection listeners that a set of changes to the collection is beginning.
* @param changeCount The number of changes anticipated, or <= 0 if unkown.
*/
protected void notifyCollectionObserversOnStart(int changeCount) {
if((collectionObservers != null) && (collectionObservers.getSize() > 0)) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).startChanges(changeCount);
}//for//
}//if//
}//notifyCollectionObserversOnStart()//
/**
* Notifies any collection listeners that the set of changes to the collection has ended.
*/
protected void notifyCollectionObserversOnStop() {
if((collectionObservers != null) && (collectionObservers.getSize() > 0)) {
for(int index = 0; index < collectionObservers.getSize(); index++) {
((IInlineCollectionObserver) collectionObservers.get(index)).stopChanges();
}//for//
}//if//
}//notifyCollectionObserversOnStop()//
/**
* Sets the flag for maintaining weak link back references used to prevent this collection from being GC'd while a collected object is being actively referenced (when the entity that references this collection flags it as part-of and weak).
*/
protected void setMaintainWeakLinkBackReferences() {
if(!maintainWeakLinkBackReferences) {
IIterator iterator = iterator();
maintainWeakLinkBackReferences = true;
while(iterator.hasNext()) {
Object next = iterator.next();
if(next instanceof ISupportsContainment) {
TypeCallbackHandler handler = MetadataService.getSingleton().getTypeMetadata(next.getClass()).getTypeCallbackHandler();
if(handler != null) {
handler.addWeakLinkBackReference(next, this);
}//if//
}//if//
}//while//
}//if//
}//setMaintainWeakLinkBackReferences()//
}//HashSet//