Initial commit from SVN.
This commit is contained in:
7
Foundation/.classpath
Normal file
7
Foundation/.classpath
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="/Common"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
18
Foundation/.project
Normal file
18
Foundation/.project
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Foundation</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>Common</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
293
Foundation/src/com/foundation/application/Application.java
Normal file
293
Foundation/src/com/foundation/application/Application.java
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.application;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.common.thread.ThreadService;
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
import com.foundation.controller.DecorationManager;
|
||||
import com.foundation.event.EventNotifier;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
import com.foundation.transaction.TransactionService;
|
||||
import com.foundation.view.resource.*;
|
||||
|
||||
public abstract class Application extends com.foundation.controller.Controller implements IApplication {
|
||||
public static final File DEFAULT_RESOURCES_PATH = new File("./");
|
||||
public static final String PACKAGE_RESOURCES_FILE_NAME = "packages.res";
|
||||
|
||||
/** The service that provides metadata relating to the models, controllers, and the mappings between the models/controllers and the repositories. */
|
||||
private MetadataService metadataService = null;
|
||||
/** The transaction service provides transactional interaction with the repositories defined in the application metadata. */
|
||||
private TransactionService transactionService = null;
|
||||
/** The service that provides resources accessable by the application at runtime. */
|
||||
private AbstractResourceService resourceService;
|
||||
/** Tracks whether the application is in the process of shutting down. */
|
||||
private boolean isShuttingDown = false;
|
||||
/** Tracks whether the application has shutdown. */
|
||||
private boolean isShutdown = false;
|
||||
/**
|
||||
* Application constructor.
|
||||
*/
|
||||
protected Application() {
|
||||
super();
|
||||
}//Application()//
|
||||
/**
|
||||
* Gets the application object that this object is running under. The application object provides the object various services that it will need.
|
||||
* @return The application reference for the application this object is running under.
|
||||
*/
|
||||
public IApplication getApplication() {
|
||||
return this;
|
||||
}//getApplication()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getDefaultRepositoryIdentifier()
|
||||
*/
|
||||
public abstract Object getDefaultRepositoryIdentifier();
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getDefaultRepositoryIdentifier(java.lang.Class)
|
||||
*/
|
||||
public Object getDefaultRepositoryIdentifier(Class type) {
|
||||
return getDefaultRepositoryIdentifier();
|
||||
}//getDefaultRepositoryIdentifier()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getDatabaseUrl(java.lang.Object)
|
||||
*/
|
||||
public String getDatabaseUrl(Object id) {
|
||||
return null;
|
||||
}//getDatabaseUrl()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getDatabaseUser(java.lang.Object)
|
||||
*/
|
||||
public String getDatabaseUser(Object id) {
|
||||
return null;
|
||||
}//getDatabaseUser()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getDatabasePassword(java.lang.Object)
|
||||
*/
|
||||
public String getDatabasePassword(Object id) {
|
||||
return null;
|
||||
}//getDatabasePassword()//
|
||||
/**
|
||||
* Gets the location of the metadata file for this application.
|
||||
* @return The java.lang.String, java.io.File, or java.net.URL location for the metadata file. If a string or file then a relative path may be specified.
|
||||
*/
|
||||
public abstract Object getMetadataLocation();
|
||||
/**
|
||||
* Gets the contents of the metadata file. This is an alternative to the getMetadataLocation() method in the event that the application cannot provide the metadata as a file reference.
|
||||
* <p>Note: This does not currently support binary metadata.</p>
|
||||
* @return The contents of the application's metadata file, or null if no metadata is provided.
|
||||
*/
|
||||
public InputStream getMetadataContents() {
|
||||
return null;
|
||||
}//getMetadataContents()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getMetadataService()
|
||||
*/
|
||||
public MetadataService getMetadataService() {
|
||||
return metadataService;
|
||||
}//getMetadataService()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.application.IApplication#getResourceService()
|
||||
*/
|
||||
public AbstractResourceService getResourceService() {
|
||||
return resourceService;
|
||||
}//getResourceService()//
|
||||
/**
|
||||
* Gets the path for the resources file that contains the package and code metadata.
|
||||
* @return The path to the package and code metadata file.
|
||||
*/
|
||||
public File getResourcesPath() {
|
||||
return DEFAULT_RESOURCES_PATH;
|
||||
}//getResourcePath()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Entity#getTransactionService()
|
||||
*/
|
||||
public TransactionService getTransactionService() {
|
||||
return transactionService;
|
||||
}//getTransactionService()//
|
||||
/**
|
||||
* This method performs the actual shutdown.
|
||||
* <p>Subclasses should override this method to add funcationality. They <b>must</b> call their super class' internalShutdown method when overriding the super class.
|
||||
*/
|
||||
protected void internalShutdown() {
|
||||
if(transactionService != null) {
|
||||
transactionService.shutdown();
|
||||
}//if//
|
||||
|
||||
EventNotifier.getSingleton().stop();
|
||||
ThreadService.shutdown();
|
||||
}//internalShutdown()//
|
||||
/**
|
||||
* Loads the property file into a new hash map of key value pairs.
|
||||
* @param propertyFilePath The absolute or relative path to the property file.
|
||||
* @return The map of key value pairs found in the property file.
|
||||
*/
|
||||
protected LiteHashMap loadProperties(File propertyFile) {
|
||||
LiteHashMap map = null;
|
||||
|
||||
if(propertyFile.exists()) {
|
||||
try {
|
||||
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(new java.io.FileReader(propertyFile));
|
||||
|
||||
map = new LiteHashMap(10);
|
||||
|
||||
while(bufferedReader.ready()) {
|
||||
String line = bufferedReader.readLine();
|
||||
int equalsIndex = line.indexOf('=');
|
||||
|
||||
if(equalsIndex >= 0) {
|
||||
String key = line.substring(0, equalsIndex).trim();
|
||||
String value = line.substring(equalsIndex + 1).trim();
|
||||
|
||||
map.put(key, value);
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: Invalid property file format found while loading the " + propertyFile.getAbsolutePath() + " property file.");
|
||||
throw new RuntimeException("Property file format exception.");
|
||||
}//else//
|
||||
}//while//
|
||||
}//try//
|
||||
catch(java.io.FileNotFoundException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(java.io.IOException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: The specified property file does not exist: " + propertyFile.getAbsolutePath());
|
||||
throw new RuntimeException("Property file does not exist.");
|
||||
}//else//
|
||||
|
||||
return map;
|
||||
}//setupMetadataService()//
|
||||
/**
|
||||
* Loads the property file into a new hash map of key value pairs.
|
||||
* @param propertyFilePath The absolute or relative path to the property file.
|
||||
* @return The map of key value pairs found in the property file.
|
||||
*/
|
||||
protected LiteHashMap loadProperties(String propertyFilePath) {
|
||||
LiteHashMap map = null;
|
||||
|
||||
if((propertyFilePath != null) && ((propertyFilePath = propertyFilePath.trim()).length() != 0)) {
|
||||
map = loadProperties(new File(propertyFilePath));
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: Unable to load a property file using a null or empty path string.");
|
||||
throw new RuntimeException("Null or empty path string is invalid. Unable to load the property file.");
|
||||
}//else//
|
||||
|
||||
return map;
|
||||
}//setupMetadataService()//
|
||||
/**
|
||||
* Initializes the metadata service for this application.
|
||||
*/
|
||||
protected boolean setupMetadataService() {
|
||||
boolean retVal = true;
|
||||
|
||||
metadataService = MetadataService.getSingleton();
|
||||
|
||||
try {
|
||||
metadataService.loadMetadata(this);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
retVal = false;
|
||||
}//catch//
|
||||
|
||||
return retVal;
|
||||
}//setupMetadataService()//
|
||||
/**
|
||||
* Initializes the transaction service for this application.
|
||||
*/
|
||||
protected void setupTransactionService() {
|
||||
transactionService = new TransactionService();
|
||||
}//setupTransactionService()//
|
||||
/**
|
||||
* Initializes the resource service for this application.
|
||||
*/
|
||||
protected void setupResourceService() {
|
||||
try {
|
||||
//Debug.log("Setting up resource service.");
|
||||
ResourceService service = new ResourceService(getResourcesPath(), null);
|
||||
//Debug.log("Created resource service: " + service);
|
||||
setResourceService(service);
|
||||
getResourceService().setCurrentCategories(new String[] {});
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//setupResourceService()//
|
||||
/**
|
||||
* Sets the resource service for this application.
|
||||
* <p>Warning: The service may only be set once. All all calls will be ignored.</p>
|
||||
* @param resourceService The application's resource service.
|
||||
*/
|
||||
protected void setResourceService(AbstractResourceService resourceService) {
|
||||
if(this.resourceService == null) {
|
||||
//Debug.log("Setting resource service: " + resourceService);
|
||||
this.resourceService = resourceService;
|
||||
}//if//
|
||||
}//setResourceService()//
|
||||
/**
|
||||
* Shuts down the application.
|
||||
* Applications should provide customized shutdown code by overriding the internalShutdown method.
|
||||
* @see #internalShutdown()
|
||||
*/
|
||||
public final synchronized void shutdown() {
|
||||
isShuttingDown = true;
|
||||
internalShutdown();
|
||||
isShutdown = true;
|
||||
isShuttingDown = false;
|
||||
notifyAll();
|
||||
}//shutdown()//
|
||||
/**
|
||||
* Determines whether the application is in the process of being shutdown (false if already shutdown or not shutting down).
|
||||
* @return Whether the application's shutdown method is running.
|
||||
*/
|
||||
public synchronized boolean isShuttingDown() {
|
||||
return isShuttingDown;
|
||||
}//isShuttingDown()//
|
||||
/**
|
||||
* Determines whether the application has been shutdown.
|
||||
* @return Whether the application's shutdown method has completed.
|
||||
*/
|
||||
public synchronized boolean isShutdown() {
|
||||
return isShutdown;
|
||||
}//isShutdown()//
|
||||
/**
|
||||
* Pauses the thread until the application shuts down.
|
||||
* <p>This method was added to support the mac osx which at the time of this writing doesn't like to have the main thread exit for any reason before the application is finished. Exiting the main thread on the mac causes the whole process to terminate immediately.</p>
|
||||
*/
|
||||
public final synchronized void waitForShutdown() {
|
||||
while(!isShutdown) {
|
||||
try {
|
||||
wait();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
}//while//
|
||||
}//waitForShutdown()//
|
||||
/**
|
||||
* Starts the application by initializing the application services and then initializing the application.
|
||||
* <p>TODO: Should provide some default startup code.</p>
|
||||
*/
|
||||
protected synchronized void startup() {
|
||||
isShutdown = false;
|
||||
}//startup()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IController#getDecorationManager()
|
||||
*/
|
||||
public final DecorationManager getDecorationManager() {
|
||||
//Never used.//
|
||||
return null;
|
||||
}//getDecorationManager()//
|
||||
}//Application//
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.foundation.application;
|
||||
|
||||
/**
|
||||
* The interface implemented by the user of an Amaranthine Defic Algorithm server. Allows the server to send updates to distributed logical objects.
|
||||
*/
|
||||
public interface IAdaClient {
|
||||
|
||||
}//IAdaClient//
|
||||
24
Foundation/src/com/foundation/application/IAdaService.java
Normal file
24
Foundation/src/com/foundation/application/IAdaService.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.foundation.application;
|
||||
|
||||
/**
|
||||
* The interface into the Amaranthine Defic Algorithm service that manages distributed locks and distribution of logical object change sets.
|
||||
*/
|
||||
public interface IAdaService {
|
||||
public void register(IAdaClient client);
|
||||
//TODO: Some way to register/unregister for notifications for certain types of logical object updates.
|
||||
/**
|
||||
*
|
||||
* Never blocks? Returns an update ID so the locker knows to ensure that the latest updates are applied before alterations are made?
|
||||
* @param type
|
||||
* @param key
|
||||
*/
|
||||
public void lockLogicalObject(String type, Object key);
|
||||
/**
|
||||
*
|
||||
* Blocks? How are failures handled?
|
||||
* @param type
|
||||
* @param key
|
||||
* @param changeSet
|
||||
*/
|
||||
public void applyLogicalObjectChanges(String type, Object key, Object changeSet);
|
||||
}//IAdaService//
|
||||
59
Foundation/src/com/foundation/application/IApplication.java
Normal file
59
Foundation/src/com/foundation/application/IApplication.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.application;
|
||||
|
||||
import com.foundation.metadata.MetadataService;
|
||||
import com.foundation.transaction.TransactionService;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
public interface IApplication extends com.foundation.controller.IController {
|
||||
/**
|
||||
* Gets the repository identifier for the application's primary (default) repository connection.
|
||||
* @return The repository identifier the application objects will use by default.
|
||||
*/
|
||||
public Object getDefaultRepositoryIdentifier();
|
||||
/**
|
||||
* Gets the repository identifier for the application's primary (default) repository connection for the given class.
|
||||
* @param type The class whose default repository identifier is desired.
|
||||
* @return The repository identifier the application objects of the given type will use by default.
|
||||
*/
|
||||
public Object getDefaultRepositoryIdentifier(Class type);
|
||||
/**
|
||||
* Gets the metadata service for this application.
|
||||
* @return The metadata service object that will service this application's metadata needs.
|
||||
*/
|
||||
public MetadataService getMetadataService();
|
||||
/**
|
||||
* Gets the resource service for this application.
|
||||
* @return The resource service object that will service this application's resource needs.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
/**
|
||||
* Gets the transaction service for this application.
|
||||
* @return The transaction service object that will service this application's transactional needs.
|
||||
*/
|
||||
public TransactionService getTransactionService();
|
||||
/**
|
||||
* Gets the URL to access the given database.
|
||||
* @param id The database/repository identifier.
|
||||
* @return The URL for the database, or null if the URL in the metadata should be used.
|
||||
*/
|
||||
public String getDatabaseUrl(Object id);
|
||||
/**
|
||||
* Gets the name of the user to connect to the database as.
|
||||
* @param id The database/repository identifier.
|
||||
* @return The database user name to use when connecting.
|
||||
*/
|
||||
public String getDatabaseUser(Object id);
|
||||
/**
|
||||
* Gets the password used to connect to the database.
|
||||
* @param id The database/repository identifier.
|
||||
* @return The database user password to use when connecting.
|
||||
*/
|
||||
public String getDatabasePassword(Object id);
|
||||
}//IApplication//
|
||||
100
Foundation/src/com/foundation/attribute/AbstractReflectData.java
Normal file
100
Foundation/src/com/foundation/attribute/AbstractReflectData.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public abstract class AbstractReflectData implements java.io.Externalizable {
|
||||
/** The object identifying the reflection. */
|
||||
long reflectionId = 0;
|
||||
/** A reference to the reflected object. This will be set by collections when creating an array of reflection data objects for contained reflectable values. */
|
||||
public IReflectable reflected = null;
|
||||
/** A reference to the reflected object's class. The caller may not know the exact type for the reflection and thus must create the reflection after receiving this reflection data object. */
|
||||
Class reflectedType = null;
|
||||
/** The signature identifying which constructor to call when creating a new instance of the collection that will become the reflection. */
|
||||
public Class[] reflectedTypeConstructorSignature = null;
|
||||
/** The parameters passed to the constructor when creating a new instance of the collection that will become the reflection. */
|
||||
public Object[] reflectedTypeConstructorParameters = null;
|
||||
/** Used to pass the handler reference to the initializing reflection. This value will NOT be serialized. */
|
||||
ReflectionContext context = null;
|
||||
/** Whether the reflection data will include the referenced object's reflection data. This may be false if updating since it is currently impossible to collect referenced object's data outside the monitor due to the need to not lose the monitor during what ever operation is causing the update. */
|
||||
private boolean includesReferencedData = false;
|
||||
/** The reflection id of the reflection in the same context and of a component object of the same logical object, which will be used track the message index for all objects in the logical object reflected by the reflection context. */
|
||||
public long mappedReflectionId = 0; //TODO: Change to 'categoryNumber' or 'categoryId'
|
||||
/** The mapping number is passed only as a result of a synchronization, and only back to the synchronizing reflection context. It allows the synchronizing reflection context to reuse the models that are new (not shared) whose copies were sent as part of the synchronize operation. This reuse allows reflections of the new objects to be maintained. */
|
||||
public int modelMappingNumber = -1;
|
||||
/**
|
||||
* AbstractReflectData constructor.
|
||||
*/
|
||||
public AbstractReflectData() {
|
||||
super();
|
||||
}//AbstractReflectData()//
|
||||
/**
|
||||
* Gets the reflected object.
|
||||
* @return The reflected object. This value may be null at any given time.
|
||||
*/
|
||||
public IReflectable getReflected() {
|
||||
return reflected;
|
||||
}//getReflected()//
|
||||
/**
|
||||
* Whether the reflection data will include the referenced object's reflection data.
|
||||
* This may be false if updating since it is currently impossible to collect referenced object's data outside the monitor due to the need to not lose the monitor during what ever operation is causing the update.
|
||||
* @return
|
||||
*/
|
||||
public boolean includesReferencedData() {
|
||||
return includesReferencedData;
|
||||
}//includesReferencedData()//
|
||||
/**
|
||||
* Whether the reflection data will include the referenced object's reflection data.
|
||||
* This may be false if updating since it is currently impossible to collect referenced object's data outside the monitor due to the need to not lose the monitor during what ever operation is causing the update.
|
||||
* @param includesReferencedData
|
||||
*/
|
||||
public void includesReferencedData(boolean includesReferencedData) {
|
||||
this.includesReferencedData = includesReferencedData;
|
||||
}//includesReferencedData()//
|
||||
/**
|
||||
* Creates the reflection from this reflection data.
|
||||
* <p>Warning: The caller must first ensure the context has been set for this data.</p>
|
||||
* @return The reflection created.
|
||||
*/
|
||||
public abstract IReflectable createReflection() throws InstantiationException, IllegalAccessException;
|
||||
/**
|
||||
* 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();
|
||||
reflectionId = in.readLong();
|
||||
reflected = (IReflectable) in.readObject();
|
||||
reflectedType = (Class) in.readObject();
|
||||
reflectedTypeConstructorSignature = (Class[]) in.readObject();
|
||||
reflectedTypeConstructorParameters = (Object[]) in.readObject();
|
||||
mappedReflectionId = in.readLong();
|
||||
modelMappingNumber = in.readInt();
|
||||
includesReferencedData = in.readBoolean();
|
||||
}//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 {
|
||||
out.writeByte(0);
|
||||
out.writeLong(reflectionId);
|
||||
out.writeObject(reflected);
|
||||
out.writeObject(reflectedType);
|
||||
out.writeObject(reflectedTypeConstructorSignature);
|
||||
out.writeObject(reflectedTypeConstructorParameters);
|
||||
out.writeLong(mappedReflectionId);
|
||||
out.writeInt(modelMappingNumber);
|
||||
out.writeBoolean(includesReferencedData);
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return reflectedType != null ? reflectedType.getName() : super.toString();
|
||||
}//toString()//
|
||||
}//AbstractReflectData//
|
||||
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.orb.Orb;
|
||||
import com.common.thread.Monitor;
|
||||
import com.common.util.EmptyIterator;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.common.util.LiteList;
|
||||
import com.common.util.optimized.LongArray;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.metadata.TypeCallbackHandler;
|
||||
|
||||
public abstract class AbstractReflectSupport extends Monitor implements IReflectSupport {
|
||||
/** The thread local used to delay reflection updates while synchronizing. */
|
||||
private static final ThreadLocal delayedReflectionUpdateHandler = new ThreadLocal();
|
||||
|
||||
/** A reference to the entity that this entity is a part of. If the entity is not a part of another entity then this will be null. */
|
||||
private transient volatile IEntity partOfEntity = null;
|
||||
/** The number of times the part of entity has been set to the same value. */
|
||||
private transient volatile int partOfEntityCounter = 0;
|
||||
/** The map used to store the next message number for use when sending updates to reflections of this object. This will be null if this object is part-of another object. */
|
||||
private LiteHashMap nextMessageIndexMap = null;
|
||||
/** The set of all ReflectionHolder instances, one for each reflection of the supported object. The reflection holder can be retrieved by using the reflection update handler (they are .equals comparable). */
|
||||
private LiteHashSet registeredReflections = null;
|
||||
/** The context object used to support this reflection. If this object is not a reflection then this value will be null. The context also handles incomming messages. */
|
||||
private ReflectionContext reflectionContext = null;
|
||||
/** The object identifying the reflection within its context. */
|
||||
private Long reflectionId = null;
|
||||
/** The category id number (which is one of the reflection id numbers issued by the reflection context to the reflected objects) which is used by the reflected object to group updates to reflections of a logical object. This value may change. */
|
||||
private long categoryNumber = 0;
|
||||
/** The hash code for the reflectable. The hash should be the same for all reflections and the reflected object. */
|
||||
private int hash = 0;
|
||||
|
||||
/**
|
||||
* Holds the message index and related data for the registered reflections in a logical object (one made up of one or more objects that reference each other as part-of).
|
||||
*/
|
||||
protected static class NextMessageHolder {
|
||||
/** The last used message index which is used to order the messages and to verify that the synchronized data was verified based on the lastest from the reflected objects. */
|
||||
public int messageIndex = 0;
|
||||
/** The category number (one of the reflection id's issued by the reflection context that created the reflections) provides the reflection's context a way to group the message index for objects that are part-of. Note that the number won't change even if the reflection with that id is released. */
|
||||
public long categoryNumber = -1;
|
||||
/** The count of how many times the holder has been referenced. This is incremented every time a reflection is created by the same reflection context within this logical object. */
|
||||
public int counter = 1;
|
||||
}//NextMessageHolder//
|
||||
|
||||
protected static abstract class AbstractReflectTypeCallbackHandler extends TypeCallbackHandler {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected int getPartOfEntityCounter(AbstractReflectSupport instance) {
|
||||
return instance.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.
|
||||
*/
|
||||
protected void setPartOfEntityCounter(AbstractReflectSupport instance, int partOfEntityCounter) {
|
||||
instance.setPartOfEntityCounter(partOfEntityCounter);
|
||||
}//setPartOfEntityCounter()//
|
||||
/**
|
||||
* Gets the entity the collection is part of.
|
||||
* @return The entity that this collection is a part of. This may be null.
|
||||
*/
|
||||
protected IEntity getPartOfEntity(AbstractReflectSupport instance) {
|
||||
return instance.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.
|
||||
*/
|
||||
protected void setPartOfEntity(AbstractReflectSupport instance, IEntity partOfEntity) {
|
||||
instance.setPartOfEntity(partOfEntity);
|
||||
}//setPartOfEntity()//
|
||||
}//AbstractReflectTypeCallbackHandler//
|
||||
|
||||
/**
|
||||
* A single update that is being delayed.
|
||||
*/
|
||||
private static class DelayedReflectionUpdate {
|
||||
public NextMessageHolder holder;
|
||||
public IReflectUpdateHandler handler;
|
||||
public long reflectionId;
|
||||
public AbstractReflectData data;
|
||||
|
||||
public DelayedReflectionUpdate(NextMessageHolder holder, IReflectUpdateHandler handler, long reflectionId, AbstractReflectData data) {
|
||||
this.holder = holder;
|
||||
this.handler = handler;
|
||||
this.reflectionId = reflectionId;
|
||||
this.data = data;
|
||||
}//DelayedReflectionUpdate()//
|
||||
}//DelayedReflectionUpdate//
|
||||
|
||||
/**
|
||||
* The handler that is used to delay a set of reflection updates and execute them at a later time.
|
||||
*/
|
||||
private static class DelayedReflectionUpdateHandler {
|
||||
protected int startCount = 1;
|
||||
private LiteList updates = new LiteList(100, 1000);
|
||||
|
||||
/**
|
||||
* DelayedReflectionUpdateHandler constructor.
|
||||
*/
|
||||
private DelayedReflectionUpdateHandler() {
|
||||
}//DelayedReflectionUpdateHandler()//
|
||||
public void addUpdate(NextMessageHolder holder, IReflectUpdateHandler handler, long reflectionId, AbstractReflectData data) {
|
||||
updates.add(new DelayedReflectionUpdate(holder, handler, reflectionId, data));
|
||||
}//addUpdate()//
|
||||
public void executeUpdates() {
|
||||
//Execute each queued update in order.//
|
||||
for(int index = 0; index < updates.getSize(); index++) {
|
||||
DelayedReflectionUpdate next = (DelayedReflectionUpdate) updates.get(index);
|
||||
int messageNumber = ++next.holder.messageIndex;
|
||||
long categoryNumber = next.holder.categoryNumber;
|
||||
|
||||
if(!Orb.isLocal(next.handler)) {
|
||||
Orb.setOneWayCall(next.handler);
|
||||
}//if//
|
||||
|
||||
//Send the reflected index and message index along with the data so that the receiver knows which reflection is being updated and what the ordering is for the update.//
|
||||
next.handler.update(next.reflectionId, next.data, messageNumber, categoryNumber);
|
||||
}//for//
|
||||
|
||||
updates.removeAll();
|
||||
}//executeUpdates()//
|
||||
}//DelayedReflectionUpdateHandler//
|
||||
|
||||
/**
|
||||
* Begins delaying all reflection updates.
|
||||
* This method may be called repeatedly, but for each call to start delaying updates there must be a corresponding call to stop delaying updates.
|
||||
* Updates will be executed when the last call to stop delaying updates is made.
|
||||
*/
|
||||
public static void startDelayingReflectionUpdates() {
|
||||
DelayedReflectionUpdateHandler handler = (DelayedReflectionUpdateHandler) delayedReflectionUpdateHandler.get();
|
||||
|
||||
if(handler == null) {
|
||||
handler = new DelayedReflectionUpdateHandler();
|
||||
delayedReflectionUpdateHandler.set(handler);
|
||||
}//if//
|
||||
else {
|
||||
handler.startCount++;
|
||||
}//else//
|
||||
}//startDelayingReflectionUpdates()//
|
||||
/**
|
||||
* Stops delaying reflection updates.
|
||||
* This method must be called exactly once for each call to start delaying updates.
|
||||
* @param sendUpdates Whether send the updates. This is only valid for the last stop call, if multiple start calls were made.
|
||||
*/
|
||||
public static void stopDelayingReflectionUpdates(boolean sendUpdates) {
|
||||
DelayedReflectionUpdateHandler handler = (DelayedReflectionUpdateHandler) delayedReflectionUpdateHandler.get();
|
||||
|
||||
if(--handler.startCount == 0) {
|
||||
delayedReflectionUpdateHandler.set(null);
|
||||
|
||||
if(sendUpdates) {
|
||||
handler.executeUpdates();
|
||||
}//if//
|
||||
}//if//
|
||||
}//stopDelayingReflectionUpdates()//
|
||||
/**
|
||||
* AbstractReflectSupport constructor.
|
||||
*/
|
||||
public AbstractReflectSupport() {
|
||||
super();
|
||||
//Should this be here? This code was pushed down from ReflectObjectSupport and ReflectCollectionSupport, but for some reason they were implemented differently.//
|
||||
//ReflectCollectionSupport had this line in the constructors. ReflectObjectSupport relied on the Entity calling it, but nothing called the entity's method.//
|
||||
//Note: I have removed this code because it fails to work when an object is deserialized and then placed in a single threaded context.
|
||||
//this.singleThreadedContext = ThreadService.getSingleThreadedContext();
|
||||
this.hash = super.hashCode();
|
||||
}//AbstractReflectSupport()//
|
||||
/**
|
||||
* AbstractReflectSupport constructor.
|
||||
* @param supported The object being supported. This is passed on to the monitor.
|
||||
*/
|
||||
public AbstractReflectSupport(Object supported) {
|
||||
super(supported);
|
||||
//Should this be here? This code was pushed down from ReflectObjectSupport and ReflectCollectionSupport, but for some reason they were implemented differently.//
|
||||
//ReflectCollectionSupport had this line in the constructors. ReflectObjectSupport relied on the Entity calling it, but nothing called the entity's method.//
|
||||
//Note: I have removed this code because it fails to work when an object is deserialized and then placed in a single threaded context.
|
||||
//this.singleThreadedContext = ThreadService.getSingleThreadedContext();
|
||||
this.hash = super.hashCode();
|
||||
}//AbstractReflectSupport()//
|
||||
/**
|
||||
* Gets the reflection's context object.
|
||||
* @return The reflection context, or null if the supported object is not a reflection.
|
||||
*/
|
||||
protected ReflectionContext getReflectionContext() {
|
||||
return reflectionContext;
|
||||
}//getReflectionContext()//
|
||||
/**
|
||||
* Sets the reflection's context object.
|
||||
* @param reflectionContext The reflection context, or null if the supported object is not a reflection.
|
||||
*/
|
||||
public void setReflectionContext(ReflectionContext reflectionContext) {
|
||||
this.reflectionContext = reflectionContext;
|
||||
}//setReflectionContext()//
|
||||
/**
|
||||
* Determines whether the supported object is a reflection.
|
||||
* @return Whether the supported collection is a reflection of another object.
|
||||
*/
|
||||
public boolean isReflection() {
|
||||
return getReflectionId() != null;
|
||||
}//isReflection()//
|
||||
/**
|
||||
* Determines whether this reflectable object is a reflection created within the given context.
|
||||
* @param reflectionContext The optional context used to check whether this is a reflection. If null then the response will be true if the object is a reflection in any context.
|
||||
* @return Whether this object is a reflection of another object and was created in the given context.
|
||||
*/
|
||||
protected boolean isReflection(ReflectionContext reflectionContext) {
|
||||
return (reflectionContext == null || reflectionContext == this.reflectionContext) && getReflectionId() != null;
|
||||
}//isReflection()//
|
||||
/**
|
||||
* Gets the reflection's identifying object.
|
||||
* @return The object which identifies the reflection within the context of the reflection context (aka reflection manager).
|
||||
*/
|
||||
protected Object getReflectionId() {
|
||||
return reflectionId;
|
||||
}//getReflectionId()//
|
||||
/**
|
||||
* Initializes this object as a reflection of another object of the same type.
|
||||
* @param reflectionData The data necessary to initialize the object as a reflection of another object.
|
||||
* @return A replacement reflection if another reflection exists for the reflected object, or null if the initialization went smoothly.
|
||||
*/
|
||||
protected Object reflectionInitialize(AbstractReflectData reflectionData) {
|
||||
//Register this reflection support with the handler.//
|
||||
reflectionContext = reflectionData.context;
|
||||
reflectionId = new Long(reflectionData.reflectionId);
|
||||
setCategoryNumber(reflectionData.mappedReflectionId);
|
||||
|
||||
return reflectionContext.register(reflectionId, this);
|
||||
}//reflectionInitialize()//
|
||||
/**
|
||||
* Destroys this object as a reflection of another object of the same type.
|
||||
* The actual object will not be destroyed, but it will no longer reflect another object.
|
||||
* <p>Warning: This method must be called for every reflection. Not calling this method may result in a memory leak.</p>
|
||||
*/
|
||||
public void destroyReflection() {
|
||||
//This value may be null if the reflection is being destroyed due to another identical reflection already existing.//
|
||||
if(reflectionContext != null) {
|
||||
//Unregister this reflection support with the handler.//
|
||||
reflectionContext.unregister(reflectionId);
|
||||
}//if//
|
||||
|
||||
reflectionId = null;
|
||||
reflectionContext = null;
|
||||
}//destroyReflection()//
|
||||
/**
|
||||
* Gets the last sent update message's number for a perticular reflection.
|
||||
* @param updateHandler The update handler for the reflection whose previous update number should be accessed.
|
||||
* @return The reflection's last sent message number.
|
||||
*/
|
||||
protected int reflectionGetLastUpdateMessageNumber(IReflectUpdateHandler updateHandler) {
|
||||
int result = -1;
|
||||
|
||||
if(getPartOfEntity() == null) {
|
||||
NextMessageHolder holder = (NextMessageHolder) nextMessageIndexMap.get(updateHandler);
|
||||
|
||||
result = holder.messageIndex;
|
||||
}//if//
|
||||
else {
|
||||
result = ((IReflectable) getPartOfEntity()).zzrReflectionGetLastUpdateMessageNumber(getPartOfEntity(), updateHandler);
|
||||
}//else//
|
||||
|
||||
if(result == -1) {
|
||||
Debug.log(new RuntimeException("Failed to locate a last update number for the given reflect update handler."));
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//reflectionGetLastUpdateMessageNumber()//
|
||||
/**
|
||||
* Initializes the update message number for this reflectable object.
|
||||
* @param updateHandler The handler for the reflection context that is creating a reflection.
|
||||
* @return The reflection identifier for the first reflection from this update handler to be created within the logical object.
|
||||
*/
|
||||
public long initializeUpdateMessageNumber(IReflectUpdateHandler updateHandler, long reflectionId) {
|
||||
long result = -1;
|
||||
|
||||
//TODO: If this object is part of a single threaded context, then delegate the next message number handling to it.
|
||||
|
||||
if(getPartOfEntity() == null) {
|
||||
NextMessageHolder holder;
|
||||
|
||||
if(nextMessageIndexMap == null) {
|
||||
nextMessageIndexMap = new LiteHashMap(20);
|
||||
}//if//
|
||||
|
||||
holder = (NextMessageHolder) nextMessageIndexMap.get(updateHandler);
|
||||
|
||||
if(holder == null) {
|
||||
holder = new NextMessageHolder();
|
||||
holder.categoryNumber = reflectionId;
|
||||
holder.messageIndex = 0;
|
||||
result = reflectionId;
|
||||
nextMessageIndexMap.put(updateHandler, holder);
|
||||
}//if//
|
||||
else {
|
||||
result = holder.categoryNumber;
|
||||
holder.counter++;
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
result = getPartOfEntity().getAttributeSupport().initializeUpdateMessageNumber(updateHandler, reflectionId);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//initializeUpdateMessageNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getCategoryNumber()
|
||||
*/
|
||||
public long getCategoryNumber() {
|
||||
return categoryNumber;
|
||||
}//getCategoryNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#setCategoryNumber(long)
|
||||
*/
|
||||
public void setCategoryNumber(long categoryNumber) {
|
||||
this.categoryNumber = categoryNumber;
|
||||
}//setCategoryNumber()//
|
||||
/**
|
||||
* Gets the next update message holder for the reflection context represented by the update handler and within this logical object.
|
||||
* @param updateHandler The update handler which represents the reflection and reflection context.
|
||||
* @return The next message number usable to send a message to the reflection.
|
||||
*/
|
||||
protected NextMessageHolder getNextUpdateMessageHolder(IReflectUpdateHandler updateHandler) {
|
||||
AbstractReflectSupport root = getLogicalObjectRoot();
|
||||
|
||||
if(root.nextMessageIndexMap == null) {
|
||||
Debug.log(new RuntimeException("Failed to locate a last update number for the given reflect update handler."));
|
||||
}//if//
|
||||
|
||||
return (NextMessageHolder) root.nextMessageIndexMap.get(updateHandler);
|
||||
}//getNextUpdateMessageHolder()//
|
||||
/**
|
||||
* Removes the next update message holder for the reflection context represented by the update handler and within this logical object.
|
||||
* @param updateHandler The update handler which represents the reflection and reflection context.
|
||||
*/
|
||||
protected void removeNextUpdateMessageHolder(IReflectUpdateHandler updateHandler) {
|
||||
AbstractReflectSupport root = getLogicalObjectRoot();
|
||||
|
||||
if(root.nextMessageIndexMap == null) {
|
||||
Debug.log(new RuntimeException("Failed to locate a last update number for the given reflect update handler."));
|
||||
}//if//
|
||||
|
||||
root.nextMessageIndexMap.remove(updateHandler);
|
||||
|
||||
if(root.nextMessageIndexMap.getSize() == 0) {
|
||||
root.nextMessageIndexMap = null;
|
||||
}//if//
|
||||
}//removeNextUpdateMessageHolder()//
|
||||
/**
|
||||
* Gets the root object for this logical object by traversing the part of entity references.
|
||||
* @return The root abstract reflect support reference for this logical object. The root object will hold the nextMessageIndexMap for the logical object.
|
||||
*/
|
||||
protected AbstractReflectSupport getLogicalObjectRoot() {
|
||||
AbstractReflectSupport result = this;
|
||||
|
||||
//Iteratively search for the top of the logical object.//
|
||||
while(result.getPartOfEntity() != null) {
|
||||
result = result.getPartOfEntity().getAttributeSupport();
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//getLogicalObjectRoot()//
|
||||
/**
|
||||
* Updates a reflection with the latest data from this reflected object.
|
||||
* @param reflectData The collection of changes.
|
||||
*/
|
||||
protected void updateReflection(ReflectionHolder holder, AbstractReflectData reflectData) {
|
||||
DelayedReflectionUpdateHandler handler = (DelayedReflectionUpdateHandler) delayedReflectionUpdateHandler.get();
|
||||
|
||||
if(handler != null) {
|
||||
handler.addUpdate(getNextUpdateMessageHolder(holder.getHandler()), holder.getHandler(), holder.getReflectionId(), reflectData);
|
||||
}//if//
|
||||
else {
|
||||
NextMessageHolder nextMessageHolder = getNextUpdateMessageHolder(holder.getHandler());
|
||||
int messageNumber = ++nextMessageHolder.messageIndex;
|
||||
long categoryNumber = nextMessageHolder.categoryNumber;
|
||||
|
||||
if(!Orb.isLocal(holder.getHandler())) {
|
||||
Orb.setOneWayCall(holder.getHandler());
|
||||
}//if//
|
||||
|
||||
//Send the reflected index and message index along with the data so that the receiver knows which reflection is being updated and what the ordering is for the update.//
|
||||
holder.getHandler().update(holder.getReflectionId(), reflectData, messageNumber, categoryNumber);
|
||||
}//else//
|
||||
}//updateReflection()//
|
||||
/**
|
||||
* Gets a reflectable proxy given the reflectable object.
|
||||
* @param reflectable The reflectable object.
|
||||
* @return The proper reflectable proxy.
|
||||
*/
|
||||
public IReflectable getReflectionProxy(IReflectable reflectable) {
|
||||
IReflectable result = null;
|
||||
|
||||
if(reflectable instanceof IReflectableObject) {
|
||||
result = (IReflectable) Orb.getProxy(reflectable, IReflectableObject.class);
|
||||
}//if//
|
||||
else {
|
||||
result = (IReflectable) Orb.getProxy(reflectable, IReflectableCollection.class);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//getReflectionProxy()//
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected 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.
|
||||
*/
|
||||
protected 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.
|
||||
*/
|
||||
protected IEntity getPartOfEntity() {
|
||||
return partOfEntity;
|
||||
}//getPartOfEntity()//
|
||||
/**
|
||||
* Provides internal, back door access to set or clear the part of entity.
|
||||
* @param entity The new entity this supported object is part-of, or null if the part-of entity should be cleared.
|
||||
*/
|
||||
protected void internalSetPartOfEntity(IEntity partOfEntity) {
|
||||
if(partOfEntity == null) {
|
||||
this.partOfEntity = null;
|
||||
setPartOfEntityCounter(0);
|
||||
}//if//
|
||||
else {
|
||||
//Leave the counter alone. We are simply swaping parents.//
|
||||
this.partOfEntity = partOfEntity;
|
||||
}//else//
|
||||
}//internalClearPartOfEntity()//
|
||||
/**
|
||||
* Sets the entity the collection is part of.
|
||||
* <p>Note that this is never called with a null value, and the caller always makes sure that the current partOfEntity is null. The counter is also handled by the caller.</p>
|
||||
* @param partOfEntity The entity that this collection is a part of. This will never be null.
|
||||
*/
|
||||
protected void setPartOfEntity(IEntity partOfEntity) {
|
||||
if(this.partOfEntity != null) {
|
||||
if(this.partOfEntity != partOfEntity) {
|
||||
throw new RuntimeException("A model or collection cannot be part-of two different objects (even within a single logical object).");
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Do anything?
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
this.partOfEntity = partOfEntity;
|
||||
|
||||
//Combine the nextMessageIndexMap with the top of the new logical object.//
|
||||
//Send the reflections a notice with the new category number.//
|
||||
if((nextMessageIndexMap != null) && (nextMessageIndexMap.getSize() > 0)) {
|
||||
AbstractReflectSupport root = getLogicalObjectRoot();
|
||||
IIterator iterator = nextMessageIndexMap.keyIterator();
|
||||
|
||||
if(root.nextMessageIndexMap == null) {
|
||||
root.nextMessageIndexMap = new LiteHashMap(20);
|
||||
}//if//
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
IReflectUpdateHandler handler = (IReflectUpdateHandler) iterator.next();
|
||||
NextMessageHolder oldHolder = (NextMessageHolder) nextMessageIndexMap.get(handler);
|
||||
NextMessageHolder newHolder = (NextMessageHolder) root.nextMessageIndexMap.get(handler);
|
||||
|
||||
if(newHolder == null) {
|
||||
root.nextMessageIndexMap.put(handler, oldHolder);
|
||||
}//if//
|
||||
else {
|
||||
newHolder.counter += oldHolder.counter;
|
||||
//Tell the reflection context to join the reflection message indices and pass the next index number for each holder so that it gets handled at the correct time.//
|
||||
handler.joinReflections(oldHolder.categoryNumber, newHolder.categoryNumber, ++oldHolder.messageIndex, ++newHolder.messageIndex);
|
||||
}//else//
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
//Clear the next message index map since it will now be maintained by the new logical object root.//
|
||||
nextMessageIndexMap = null;
|
||||
}//else//
|
||||
}//setPartOfEntity()//
|
||||
/**
|
||||
* Collects the reflect update handlers and associates them with the LongArray of refection id numbers for the objects reflected by the handler.
|
||||
* <p>Note: This method normally kicks off the call to internalCollectReflectUpdateHandlers which recursively searches for reflect update handlers.</p>
|
||||
* @param reflectedMap The map of LongArray's containing reflection id's, indexed by the IReflectUpdateHandler that created the reflections.
|
||||
*/
|
||||
protected void collectReflectUpdateHandlers(LiteHashMap reflectedMap) {
|
||||
internalCollectReflectUpdateHandlers(reflectedMap);
|
||||
}//collectReflectUpdateHandlers()//
|
||||
/** TODO: Make this protected..
|
||||
* Collects the reflect update handlers and associates them with the LongArray of refection id numbers for the objects reflected by the handler.
|
||||
* <p>Note: This method should be overloaded to provide recursion such that all child objects in the logical obecjt are searched.</p>
|
||||
* @param reflectedMap The map of LongArray's containing reflection id's, indexed by the IReflectUpdateHandler that created the reflections.
|
||||
*/
|
||||
public void internalCollectReflectUpdateHandlers(LiteHashMap reflectedMap) {
|
||||
IIterator iterator = getRegisteredReflections();
|
||||
|
||||
//Search through the reflections of this object.//
|
||||
while(iterator.hasNext()) {
|
||||
ReflectionHolder next = (ReflectionHolder) iterator.next();
|
||||
LongArray array = (LongArray) reflectedMap.get(next);
|
||||
|
||||
if(array == null) {
|
||||
array = new LongArray(20, 100);
|
||||
reflectedMap.put(next.getHandler(), array);
|
||||
}//if//
|
||||
|
||||
array.add(next.getReflectionId());
|
||||
}//while//
|
||||
}//internalCollectReflectUpdateHandlers()//
|
||||
/**
|
||||
* Determines whether the supported object has any active reflections.
|
||||
* @return Whether there are any reflections of the supported object.
|
||||
*/
|
||||
protected boolean hasReflections() {
|
||||
return (registeredReflections != null) && (registeredReflections.getSize() > 0);
|
||||
}//hasReflections()//
|
||||
/**
|
||||
* Determines whether this object is reflected in the given reflection context (represented by the update handler).
|
||||
* @param updateHandler The handler that represents the requesting reflection context.
|
||||
* @return Whether there is already a reflection for this reflectable object in the given context.
|
||||
*/
|
||||
public boolean isReflected(IReflectUpdateHandler updateHandler) {
|
||||
return registeredReflections != null && registeredReflections.containsValue(updateHandler);
|
||||
}//isReflected()//
|
||||
/**
|
||||
* Gets the reflection holder for the previously registered reflection.
|
||||
* @param updateHandler The update handler that represents the previously registered reflection.
|
||||
* @return The holder for the reflection releated data.
|
||||
*/
|
||||
protected ReflectionHolder getRegisteredReflection(IReflectUpdateHandler updateHandler) {
|
||||
return registeredReflections != null ? (ReflectionHolder) registeredReflections.get(updateHandler) : null;
|
||||
}//getRegisteredReflection()//
|
||||
/**
|
||||
* Sets the reflection holder for the registering reflection.
|
||||
* @param holder The holder that represents the registering reflection.
|
||||
*/
|
||||
protected void setRegisteredReflection(ReflectionHolder holder) {
|
||||
if(registeredReflections == null) {
|
||||
registeredReflections = new LiteHashSet(10);
|
||||
}//if//
|
||||
|
||||
registeredReflections.add(holder);
|
||||
}//setRegisteredReflection()//
|
||||
/**
|
||||
* Gets the ReflectionHolder instances for the registered reflections.
|
||||
* @return An iterator over the registered reflection's holders.
|
||||
*/
|
||||
protected IIterator getRegisteredReflections() {
|
||||
return registeredReflections == null ? EmptyIterator.getSingleton() : registeredReflections.iterator();
|
||||
}//getRegisteredReflections()//
|
||||
/**
|
||||
* Unregisters a refection.
|
||||
* @param refection The refection to be unregistered.
|
||||
*/
|
||||
public void unregisterReflection(IReflectUpdateHandler updateHandler) {
|
||||
if(hasReflections()) {
|
||||
ReflectionHolder reflectionHolder = (ReflectionHolder) registeredReflections.get(updateHandler);
|
||||
NextMessageHolder nextMessageHolder = getNextUpdateMessageHolder(updateHandler);
|
||||
|
||||
//TODO: If this object is part of a single threaded context, then delegate the next message number handling to it.
|
||||
|
||||
if(reflectionHolder != null && nextMessageHolder != null) {
|
||||
registeredReflections.remove(updateHandler);
|
||||
reflectionHolder.unregisterDisconnectHandler();
|
||||
ReflectionContext.getReflectionIdHolder(updateHandler).releaseId();
|
||||
|
||||
//Clean up after the next message holder map.//
|
||||
if(--nextMessageHolder.counter == 0) {
|
||||
removeNextUpdateMessageHolder(updateHandler);
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
if(reflectionHolder == null) {
|
||||
Debug.log(new RuntimeException("Error: Unable to unregister the collection reflection. Could not locate the ReflectionHolder."));
|
||||
}//if//
|
||||
else {
|
||||
Debug.log(new RuntimeException("Error: Unable to unregister the collection reflection. Could not locate the NextMessageHolder."));
|
||||
}//else//
|
||||
}//else//
|
||||
|
||||
if(registeredReflections.getSize() == 0) {
|
||||
registeredReflections = null;
|
||||
}//if//
|
||||
}//if//
|
||||
}//unregisterReflection()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
AbstractReflectSupport clone = (AbstractReflectSupport) super.clone();
|
||||
|
||||
clone.partOfEntity = null;
|
||||
clone.partOfEntityCounter = 0;
|
||||
clone.registeredReflections = null;
|
||||
clone.reflectionContext = null;
|
||||
clone.reflectionId = null;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/**
|
||||
* Encapsulates cloning a value.
|
||||
* <p>Note: The reason we return the original object if it is not cloneable is because it could be immutable and we simply have no way of knowing. We will for now assume it is immutable.</p>
|
||||
* @param value The value to be cloned.
|
||||
* @return The cloned value, or the original value if the value was not clonable.
|
||||
*/
|
||||
protected Object cloneValue(Object value) {
|
||||
try {
|
||||
if(value instanceof com.foundation.clone.ICloneable) {
|
||||
value = ((com.foundation.clone.ICloneable) value).cloneObject(null, null);
|
||||
}//if//
|
||||
else if(value instanceof java.util.Date) {
|
||||
value = ((java.util.Date) value).clone();
|
||||
}//else if//
|
||||
else {
|
||||
//Do nothing, assume the value is immutable.//
|
||||
//There is currently no way to call the clone method, even if the Cloneable interface is implemented.//
|
||||
}//else//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
//This should not normally occur.//
|
||||
Debug.log(e);
|
||||
}//catc//
|
||||
|
||||
return value;
|
||||
}//cloneValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getHash()
|
||||
*/
|
||||
public int getHash() {
|
||||
return hash;
|
||||
}//getHash()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#setHash(int)
|
||||
*/
|
||||
public void setHash(int hash) {
|
||||
this.hash = hash;
|
||||
}//setHash()//
|
||||
}//AbstractReflectSupport//
|
||||
3512
Foundation/src/com/foundation/attribute/AttributeSupport.java
Normal file
3512
Foundation/src/com/foundation/attribute/AttributeSupport.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.orb.Orb;
|
||||
import com.common.thread.Monitor;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.common.MetadataContainer;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Note: We don't track which objects were reflected since that information can easily be obtained by asking the reflectable object if it has a ReflectionHolder for the updateHandler.
|
||||
* Also note that ReflectionHolder is comparable with IReflectUpdateHandler since that is how it is hashed and indexed.
|
||||
* </p>
|
||||
*/
|
||||
public class CreateReflectDataContext {
|
||||
/** The update handler which is called when the reflected object changes. This must be the same or comparable value used to unregister the reflection. */
|
||||
private IReflectUpdateHandler updateHandler;
|
||||
/** The optional metadata container used to determine what is to be reflected. */
|
||||
private MetadataContainer metadataContainer;
|
||||
/** The optional metadata container's index used to lookup the metadata container. */
|
||||
private String metadataIndex = null;
|
||||
/** The identifier holder for the reflection context. */
|
||||
private ReflectionContext.IdHolder idHolder = null;
|
||||
/** The collection of reflectable objects that need to be reflected, but do not share a monitor with the logical object currently being reflected. */
|
||||
private LiteList reflectionDataContainer;
|
||||
/** The collection of reflectable objects that need to be reflected, but do not share a monitor with the logical object currently being reflected. */
|
||||
private LiteList pendingExternalReflectables;
|
||||
/** The monitor currently locked. This is used to determine whether reflectable objects that need reflecting should be inlined in this context or held for processing later. */
|
||||
private Monitor monitor = null;
|
||||
/** A flag that tells the code that creates the reflection data to reflect all attributes and look for model identifiers. If this is false then the metadata will first be used if available, otherwise standard reflection rules apply. This flag allows the synchronizing reflection context to receive reflection data for new objects that includes all possible attribute values. */
|
||||
private boolean isSynchronizationResult = false;
|
||||
/**
|
||||
* CreateReflectDataContext constructor.
|
||||
* @param updateHandler
|
||||
* @param metadataContainer
|
||||
* @param metadataIndex
|
||||
* @param monitor
|
||||
* @param isSynchronizationResult Whether the context should be reflecting all possible attributes for the objects it creates reflections of.
|
||||
*/
|
||||
public CreateReflectDataContext(IReflectUpdateHandler updateHandler, MetadataContainer metadataContainer, String metadataIndex, Monitor monitor, boolean isSynchronizationResult) {
|
||||
super();
|
||||
this.monitor = monitor;
|
||||
this.updateHandler = updateHandler;
|
||||
this.metadataContainer = metadataContainer;
|
||||
this.metadataIndex = metadataIndex;
|
||||
this.reflectionDataContainer = new LiteList(10);
|
||||
this.pendingExternalReflectables = new LiteList(10);
|
||||
this.isSynchronizationResult = isSynchronizationResult;
|
||||
}//CreateReflectDataContext()//
|
||||
/**
|
||||
* Gets the flag telling the code that creates the reflection data to reflect all attributes.
|
||||
* If this is false then the metadata will first be used if available, otherwise standard reflection rules apply.
|
||||
* This flag allows the synchronizing reflection context to receive reflection data for new objects that includes all possible attribute values.
|
||||
* @return Whether the context should be reflecting all possible attributes for the objects it creates reflections of.
|
||||
*/
|
||||
public boolean isSynchronizationResult() {
|
||||
return isSynchronizationResult;
|
||||
}//isSynchronizationResult()//
|
||||
/**
|
||||
* Gets the identifier holder and generator for the reflection context whether that context is remote or local.
|
||||
* @return The holder that can generate unique identifiers within the reflection context.
|
||||
*/
|
||||
public ReflectionContext.IdHolder getIdHolder() {
|
||||
if(idHolder == null) {
|
||||
idHolder = ReflectionContext.getReflectionIdHolder(updateHandler);
|
||||
}//if//
|
||||
|
||||
return idHolder;
|
||||
}//getIdHolder()//
|
||||
/**
|
||||
* Gets the update handler which is called when the reflected object changes. This must be the same or comparable value used to unregister the reflection.
|
||||
* @return The update handler that represents the caller requesting the reflection(s).
|
||||
*/
|
||||
public IReflectUpdateHandler getUpdateHandler() {
|
||||
return updateHandler;
|
||||
}//getUpdateHandler()//
|
||||
/**
|
||||
* Gets the optional metadata container used to determine what is to be reflected.
|
||||
* @return The metadata describing what to reflect.
|
||||
*/
|
||||
public MetadataContainer getMetadataContainer() {
|
||||
return metadataContainer;
|
||||
}//getMetadataContainer()//
|
||||
/**
|
||||
* Gets the optional metadata container index used to get the metadata container on any process.
|
||||
* @return The index of the metadata describing what to reflect.
|
||||
*/
|
||||
public String getMetadataIndex() {
|
||||
return metadataIndex;
|
||||
}//getMetadataIndex()//
|
||||
/**
|
||||
* Gets the collection of AbstractReflectData instances, one for each reflection generated by this context.
|
||||
* @return The collection of reflection data generated by this context.
|
||||
*/
|
||||
public LiteList getReflectionDataContainer() {
|
||||
return reflectionDataContainer;
|
||||
}//getReflectionDataContainer()//
|
||||
/**
|
||||
* Gets the collection of reflectable objects that need to be reflected, but do not share a monitor with the logical object currently being reflected.
|
||||
* @return The collection of pending reflectable objects that should be reflected under a separate lock.
|
||||
*/
|
||||
public LiteList getPendingExternalReflectables() {
|
||||
return pendingExternalReflectables;
|
||||
}//getPendingExternalReflectables()//
|
||||
/**
|
||||
* Processes all pending external reflectables by registering reflections of them.
|
||||
* The results are added to the reflection data container.
|
||||
* <p>Warning: This should always be called after releasing any locks and needing to access the reflection data container.</p>
|
||||
*/
|
||||
public void processPendingExternalReflectables() {
|
||||
if(pendingExternalReflectables.getSize() > 0) {
|
||||
ReflectRegistrationData registrationData = getMetadataIndex() != null ? new ReflectRegistrationData(getMetadataIndex()) : new ReflectRegistrationData(getMetadataContainer());
|
||||
|
||||
//TODO: The single threaded context code must be moved. It cannot be here because a proxy might be to an object in a single threaded context and this code won't run it on the context's thread.
|
||||
|
||||
//Handle remote reflectables.
|
||||
//Handle reflectables with different monitors.
|
||||
//Handle reflectables that are reflections or are in single threaded contexts.
|
||||
for(int index = 0; index < pendingExternalReflectables.getSize(); index++) {
|
||||
IReflectable reflectable = (IReflectable) pendingExternalReflectables.get(index);
|
||||
ReflectDataContainer result = null;
|
||||
|
||||
if(Orb.isLocal(reflectable)) {
|
||||
/*
|
||||
ISingleThreadedContext singleThreadedContext = reflectable.getSingleThreadedContext();
|
||||
|
||||
if(singleThreadedContext != null) {
|
||||
result = singleThreadedContext.execute(new IRunnable() {
|
||||
public Object run() {
|
||||
return reflectable.reflectionRegister(getUpdateHandler(), registrationData);
|
||||
}//run()//
|
||||
});
|
||||
}//if//
|
||||
else {
|
||||
ThreadService.ResultRunnable runnable = new ThreadService.ResultRunnable(new IRunnable() {
|
||||
public Object run() {
|
||||
return reflectable.reflectionRegister(getUpdateHandler(), registrationData);
|
||||
}//run()//
|
||||
});
|
||||
ThreadService.run(runnable);
|
||||
runnable.block();
|
||||
result = runnable.getResult();
|
||||
}//else//
|
||||
*/
|
||||
result = reflectable.zzrReflectionRegister(getUpdateHandler(), registrationData);
|
||||
}//if//
|
||||
else {
|
||||
result = reflectable.zzrReflectionRegister((IReflectUpdateHandler) (Orb.isProxy(getUpdateHandler()) ? getUpdateHandler() : Orb.getProxy(getUpdateHandler(), IReflectUpdateHandler.class)), registrationData);
|
||||
}//else//
|
||||
|
||||
//Ignore the result if it is null since it is already in the calling context.//
|
||||
if(result != null) {
|
||||
reflectionDataContainer.addAll(((ReflectDataContainer) result).getReflectionData());
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//processPendingExternalReflectables()//
|
||||
/**
|
||||
* Includes the reflectable in the creation of reflection data so that its reflection data will be sent.
|
||||
* <p>Note: This method may proccess the reflection data immediatly, or may delay the processing until later.</p>
|
||||
* @param reflectable The reflectable whose reflection data should be generated.
|
||||
*/
|
||||
public void include(IReflectable reflectable) {
|
||||
if((Orb.isLocal(reflectable)) && ((monitor == null) || (reflectable.getMonitor() == monitor))) {
|
||||
reflectable.zzrReflectionLocalRegister(Orb.isProxy(reflectable) ? Orb.getLocal(reflectable) : reflectable, this);
|
||||
}//if//
|
||||
else {
|
||||
getPendingExternalReflectables().add(reflectable);
|
||||
}//else//
|
||||
}//include()//
|
||||
}//CreateReflectDataContext//
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 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.attribute;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
/**
|
||||
* The custom lazy load handler that may be used with models supported by AttributeSupport.
|
||||
*/
|
||||
public interface ICustomLazyLoadHandler {
|
||||
/**
|
||||
* Lazy loads the passed attribute for the given object that defines the attribute.
|
||||
* @param model The model object defining the attribute. This primarily allows one handler to handle many different objects.
|
||||
* @param attribute The attribute being lazily loaded. This primarily allows one handler to handle many different attributes.
|
||||
* @return The value for the given attribute and object.
|
||||
*/
|
||||
public Object lazyLoadAttribute(Object model, Attribute attribute);
|
||||
}//ICustomLazyLoadHandler//
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public interface IReflectCollectionSupport extends IReflectSupport {
|
||||
/**
|
||||
* Synchronizes changes from a refection of this object.
|
||||
* @param updateHandler The handler used to identify the reflection that is performing the synchronization of data. The synchronizing reflection will only receive notification of the changes through this method's return value.
|
||||
* @param data The collection of added and removed values.
|
||||
* @return The collection of changes actually applied to the collection.
|
||||
*/
|
||||
public ReflectCollectionData synchronizeReflection(IReflectUpdateHandler updateHandler, AbstractReflectData data);
|
||||
/**
|
||||
* Unregisters a refection.
|
||||
* @param refection The refection to be unregistered.
|
||||
*/
|
||||
public void unregisterReflection(IReflectUpdateHandler refection);
|
||||
}//IReflectCollectionSupport//
|
||||
73
Foundation/src/com/foundation/attribute/IReflectSupport.java
Normal file
73
Foundation/src/com/foundation/attribute/IReflectSupport.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.foundation.metadata.CloneContext;
|
||||
|
||||
public interface IReflectSupport {
|
||||
/**
|
||||
* Completes the synchronization process by clearing the add/remove buffers and applying any actual changes, and rolling back changes that did not get applied.
|
||||
* @param proposedChanges The changes proposed by this reflection.
|
||||
* @param appliedChanges The changes actually applied to the reflected object.
|
||||
*/
|
||||
public void completeSynchronization(AbstractReflectData proposedChanges, AbstractReflectData appliedChanges);
|
||||
/**
|
||||
* Destroys this reflection so that it no longer reflects another object of the same type.
|
||||
* The actual object will not be destroyed, but it will no longer reflect another object.
|
||||
* <p>Warning: This method must be called for every reflection. Not calling this method may result in a memory leak.</p>
|
||||
*/
|
||||
public void destroyReflection();
|
||||
/**
|
||||
* Gets the object being reflected.
|
||||
* @return The reflected object.
|
||||
*/
|
||||
public IReflectable getReflectedObject();
|
||||
/**
|
||||
* Gets the supported object that is a reflection.
|
||||
* @return The object being supported.
|
||||
*/
|
||||
public IReflectable getReflectionObject();
|
||||
/**
|
||||
* Gets the supported object.
|
||||
* @return The object supported by this support object.
|
||||
*/
|
||||
public IReflectable getSupportedObject();
|
||||
/**
|
||||
* Gets the data necessary to synchronize the reflection changes.
|
||||
* @return A collection of changes that can be applied to the reflected object, or null if there are no changes, and thus no need to synchronize.
|
||||
*/
|
||||
public AbstractReflectData getSynchronizationData(CloneContext cloneContext);
|
||||
/**
|
||||
* Updates the supported object given a collection of changes that were made to the reflected object.
|
||||
* <p>Note: Since the user thread should have locked the changable data (in some application dependant way) so that it is the only user thread that can make the changes, we don't really have
|
||||
* to worry about synchronization. We still have to synchronize on the entity when making the changes so that caches are properly updated. The reason why two user threads can't make changes
|
||||
* at the same time is one would over write the other's changes at best, at worst they would cause an inconsistent set of clones (some clones may not properly reflect the entity's state).</p>
|
||||
* @param reflectData The collection of changes.
|
||||
*/
|
||||
public void updateReflection(AbstractReflectData reflectData);
|
||||
/**
|
||||
* Gets the category number used by the reflection context to group reflections into logical objects which receive updates using a common ordering index.
|
||||
* @return The number used to group a set of reflections into a logical object since not all components of a logical object may be reflected within the context.
|
||||
*/
|
||||
public long getCategoryNumber();
|
||||
/**
|
||||
* Gets the category number used by the reflection context to group reflections into logical objects which receive updates using a common ordering index.
|
||||
* @param categoryNumber The number used to group a set of reflections into a logical object since not all components of a logical object may be reflected within the context.
|
||||
*/
|
||||
public void setCategoryNumber(long categoryNumber);
|
||||
/**
|
||||
* Gets the hash for the supported reflectable.
|
||||
* @return The hash number for the reflectable. This number will be the same for all reflections and the reflected object.
|
||||
*/
|
||||
public int getHash();
|
||||
/**
|
||||
* Sets the hash for the supported reflectable.
|
||||
* @param hash The hash number for the reflectable. This number will be the same for all reflections and the reflected object.
|
||||
*/
|
||||
public void setHash(int hash);
|
||||
}//IReflectSupport//
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public interface IReflectUpdateHandler {
|
||||
/**
|
||||
* Updates the values of the supported reflection given the set of changes.
|
||||
* @param reflectionId The identifier for the reflection that is being updated.
|
||||
* @param reflectData The collection of added and/or removed values.
|
||||
* @param messageIndex The order index allowing the handler to process updates in order.
|
||||
* @param messageCategory The category number used to qualify the message index. The category number will be one of the reflection id's issued by the reflection context.
|
||||
*/
|
||||
public void update(long reflectionId, AbstractReflectData reflectData, int messageIndex, long messageCategory);
|
||||
/**
|
||||
* Gets the reflection context's next available process id number.
|
||||
* @return The next process id number. This will then be incremented by the reflection context.
|
||||
*/
|
||||
public int getNextProcessId();
|
||||
/**
|
||||
* Joins two message index categories together since the reflected objects are now part of the same logical object.
|
||||
* The code should only perform the join when the old category processes all messages up to the old message index, and the new category processes all messages to the new message index.
|
||||
* @param oldCategoryNumber The message index category that is going away.
|
||||
* @param newCategoryNumber The message index category that the old category will join with.
|
||||
* @param oldMessageIndex The index of the message (order it should be processed) within the old category.
|
||||
* @param newMessageIndex The index of the message (order it should be processed) within the joined category.
|
||||
*/
|
||||
public void joinReflections(long oldCategoryNumber, long newCategoryNumber, int oldMessageIndex, int newMessageIndex);
|
||||
}//IReflectUpdateHandler//
|
||||
143
Foundation/src/com/foundation/attribute/IReflectable.java
Normal file
143
Foundation/src/com/foundation/attribute/IReflectable.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.metadata.ISupportsContainment;
|
||||
|
||||
public interface IReflectable extends com.foundation.clone.ICloneable, ISupportsContainment {
|
||||
/**
|
||||
* Gets the reference to the reflected object. If the reflected object is remote, then this will be a proxy to the reflected object.
|
||||
* <p>Note: Reflections are not serializable, but the reflected reference may be serializable.</p>
|
||||
* <p>Note: This method is not intended for remote use, but is included in this interface for convenience sake since it is called a lot.</p>
|
||||
* @return A reference to the object being reflected, or null if this is not a reflection of another object.
|
||||
*/
|
||||
public IReflectable getReflected();
|
||||
/**
|
||||
* Determines whether this reflectable object is a reflection.
|
||||
* <p>Note: Reflections are not serializable, but the reflected reference may be serializable.</p>
|
||||
* <p>Note: This method is not intended for remote use, but is included in this interface for convenience sake since it is called a lot.</p>
|
||||
* @return Whether this object is a reflection of another object.
|
||||
*/
|
||||
public boolean isReflection();
|
||||
/**
|
||||
* Determines whether this reflectable object is a reflection.
|
||||
* <p>Note: Reflections are not serializable, but the reflected reference may be serializable.</p>
|
||||
* <p>Note: This method is not intended for remote use, but is included in this interface for convenience sake since it is called a lot.</p>
|
||||
* @param reflectionContext The optional reflection context. If provided then the result will only be true if this is a reflection within the given reflection context.
|
||||
* @return Whether this object is a reflection of another object.
|
||||
*/
|
||||
public boolean isReflection(ReflectionContext reflectionContext);
|
||||
/**
|
||||
* Gets the reflection context associated with this reflectable.
|
||||
* @return The reflection context associated with this reflectable object, or null if the object is not a reflection.
|
||||
*/
|
||||
public ReflectionContext getReflectionContext();
|
||||
/**
|
||||
* Registers a reflection given the reflection's update handler reference.
|
||||
* @param updateHandler A reference to the reflection's update handler.
|
||||
* @param registrationData The data used to register a reflection.
|
||||
* @return The data necessary to create the reflection(s). This is either a ReflectDataContainer instance containing the reflection data where the first is the reflection being registered, or it is null.
|
||||
*/
|
||||
public ReflectDataContainer zzrReflectionRegister(IReflectUpdateHandler updateHandler, ReflectRegistrationData registrationData);
|
||||
/**
|
||||
* Synchronizes data from a reflection to this reflected object.
|
||||
* <p>Note: The synchronizing reflection will only receive notification of the changes through this method's return value. An event will not be sent via the update handler.
|
||||
* The returned data will contain the last update message number sent to the reflection. This can be used to ensure that messages are processed in order.</p>
|
||||
* @param updateHandler The handler used to identify the reflection that is performing the synchronization of data.
|
||||
* @param data The set of changed data.
|
||||
* @param createReflectDataContext The context for creating reflections of the IReflectable changes for the synchronizing reflection context.
|
||||
* @return The collection of changes actually applied to the reflected object.
|
||||
*/
|
||||
public AbstractReflectData reflectionSynchronize(IReflectUpdateHandler updateHandler, AbstractReflectData data, CreateReflectDataContext createReflectDataContext);
|
||||
/**
|
||||
* Unregisters a previously registered reflection.
|
||||
* @param updateHandler A reference to the reflection's update handler.
|
||||
*/
|
||||
public void reflectionUnregister(IReflectUpdateHandler updateHandler);
|
||||
/**
|
||||
* Determines whether this reflectable object is a reflection created within the given context.
|
||||
* <p>Note: Reflections are not serializable, but the reflected reference may be serializable.</p>
|
||||
* @param reflectionContext The optional context used to check whether this is a reflection. If null then the response will be true if the object is a reflection in any context.
|
||||
* @return Whether this object is a reflection of another object and was created in the given context.
|
||||
*/
|
||||
public boolean zzrIsReflection(Object reflectable, ReflectionContext reflectionContext);
|
||||
/**
|
||||
* Gets the reflection context if this is a reflection.
|
||||
* @return The reflection context that created this reflection, or null if this isn't a reflection.
|
||||
*/
|
||||
public ReflectionContext zzrGetReflectionContext(Object reflectable);
|
||||
/**
|
||||
* Gets the reflection's identifying object.
|
||||
* @return The object which identifies the reflection within the context of the reflection context (aka reflection manager).
|
||||
*/
|
||||
public Object zzrGetReflectionId(Object reflectable);
|
||||
/**
|
||||
* Creates a 'reflection safe' copy of this reflectable object.
|
||||
* That means each attribute (or contained value in the case of collections) must be examined.
|
||||
* If the value is an IReflectable and is a reflection then it must be dereferenced (the reflected reference used in its stead).
|
||||
* If the value is an IReflectable and it isn't a reflection then it must have a safe copy made and used in place of the value.
|
||||
* All other value types are simply referenced as is.
|
||||
* <p>This is useful when synchronizing a reflection which references a new object which may reference other new objects or existing objects (as reflections).
|
||||
* In such a case the synchronized object notices that it has a non-reflection value reference that should be a reflection and it assumes that it is new.
|
||||
* The new value needs to be copied so that the copy doesn't have preconcieved notions of its monitor
|
||||
* @param reflectionContext The reflection context for the copy.
|
||||
* @return The safe copy of this reflection, which can either be the reflected value, or a copy of this non-reflected value.
|
||||
*/
|
||||
public IReflectable zzrReflectionCreateSafeCopy(Object reflectable, ReflectionContext reflectionContext);
|
||||
/**
|
||||
* Recursively prepares the reflectable (non-reflection) prior to being part of a synchronize action via the current reflection context.
|
||||
* <p>For example, if a view creates a new model object and makes it part of an existing reflection of a logical model, then this method will be called on the new model object prior to synchronizing the changes.</p>
|
||||
* <p>Note: This method only gets called if this object is not a reflection in the current context (in which case it is never going to be a reflection at all), but is referenced by a reflection that is being synchronized.
|
||||
* This method should prepare a non-reflection for being moved to a different reflection context (or no reflection context) by replacing any references to reflections in the current context.</p>
|
||||
* <p>This method does not copy this object, but does make permenant changes.</p>
|
||||
* @param currentContext The current reflection context that is preparing to synchronize this object. This object might not be a reflection in the current context if this object is newly created.
|
||||
* @param destinationContext The reflection context that this object is being synchronized to, if known. This may be null if the context is remote, or if not synchronizing to another reflection context. This is primarily used to make coding views more flexable since one view may base some but not all its data on the previous view's reflections.
|
||||
* @param newPartOfEntity The part of entity reference that should be set during a recursive call. Initial callers should ALWAYS pass null.
|
||||
* @param cloned The reflectable that this reflectable is a clone of. All non-reflections are cloned prior to synchronization so that the new shared objects will not have any ties to old proxies. Proxies to the non-clone will continue to work since the synchronization will either fail (in which case the proxied non-clone object doesn't change), or will succeed and pass the reflection data back to the reflection context which will utilize the non-clone object as the reflection object.
|
||||
* @param synchronizingContext The reflection context that is performing the synchronization operation.
|
||||
*/
|
||||
public void zzrReflectionPreSynchronize(Object reflectable, ReflectionContext currentContext, ReflectionContext destinationContext, IEntity newPartOfEntity, IReflectable cloned, ReflectionContext synchronizingContext);
|
||||
/**
|
||||
* Destroys this object as a reflection of another object of the same type.
|
||||
* The actual object will not be destroyed, but it will no longer reflect another object.
|
||||
* <p>Warning: This method must be called for every reflection. Not calling this method may result in a memory leak.</p>
|
||||
*/
|
||||
public void zzrReflectionDestroy(Object reflectable);
|
||||
/**
|
||||
* Initializes this object as a reflection of another object of the same type.
|
||||
* @param reflectionData The data necessary to initialize the reflection.
|
||||
* @param reflectionContext The context that the reflection exists within.
|
||||
* @return A replacement reflection if another reflection exists for the reflected object, or null if the initialization went smoothly.
|
||||
*/
|
||||
public Object zzrReflectionInitialize(Object reflectable, AbstractReflectData reflectionData);
|
||||
/**
|
||||
* Called after the reflectionInitialize method and after all reflections in a bundle have been initialized.
|
||||
* Allows the reflection to replace reflected references with their reflections.
|
||||
* @param reflectionData The data necessary to initialize the reflection.
|
||||
*/
|
||||
public void zzrReflectionPostInitialize(Object reflectable, AbstractReflectData reflectionData);
|
||||
/**
|
||||
* Performs a local registration of a new reflection for this reflectable.
|
||||
* This is local because it is caused by the container (parent or object that this object is part-of) being reflected.
|
||||
* @param createReflectDataContext The context for the reflection creation.
|
||||
*/
|
||||
public void zzrReflectionLocalRegister(Object reflectable, CreateReflectDataContext createReflectDataContext);
|
||||
/**
|
||||
* Gets the last sent update message's number for a perticular reflection.
|
||||
* @param updateHandler The update handler for the reflection whose previous update number should be accessed.
|
||||
* @return The reflection's last sent message number.
|
||||
*/
|
||||
public int zzrReflectionGetLastUpdateMessageNumber(Object reflectable, IReflectUpdateHandler updateHandler);
|
||||
/**
|
||||
* Collects the IReflectable instances that should be reflected and returned to the synchronizing reflection context, upon completing a synchronization.
|
||||
* @param reflectable The reflectable instance that this interface supports.
|
||||
* @param createReflectDataContext The context used to add IReflectable objects for later reflecting.
|
||||
*/
|
||||
public void zzrCollectPostSynchronizeReflectables(Object reflectable, CreateReflectDataContext createReflectDataContext);
|
||||
}//IReflectable//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public interface IReflectableCollection extends IReflectable {
|
||||
}//IReflectableCollection//
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public interface IReflectableObject extends IReflectable {
|
||||
/**
|
||||
* Loads the specified attribute value.
|
||||
* @param attributeName The name of the attribute whose value is to be retrieved.
|
||||
* @param updateHandler The update handler to be used if the value is reflectable.
|
||||
* @return The attribute value, or if the value is reflectable, the data necessary to create the reflection(s). This is either an object, or a ReflectDataContainer instance containing the reflection data where the first is the reflection being registered, or it is an IReflectable instance or proxy if the instance is already reflected in the calling context.
|
||||
*/
|
||||
public Object reflectionLoadAttribute(String attributeName, IReflectUpdateHandler updateHandler, ReflectRegistrationData reflectionData);
|
||||
}//IReflectableObject//
|
||||
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.common.util.optimized.IntArray;
|
||||
|
||||
/**
|
||||
* This class is designed to transfer collection values to a new reflection of the collection, and to transfer changes to a collection reflection.
|
||||
*/
|
||||
public class ReflectCollectionData extends AbstractReflectData {
|
||||
private static final byte OPTION_READ_ONLY = 0x01;
|
||||
private static final byte OPTION_PART_OF = 0x02;
|
||||
|
||||
/** This operation has no followup values and places nothing in the values collection. */
|
||||
public static final byte OPERATION_REMOVE_ALL = 0x01;
|
||||
/** For un-indexed collections this will be followed by the number of elements in the values collection, otherwise it will be followed by the number of elements in the operations collection - one per index removed. */
|
||||
public static final byte OPERATION_REMOVE = 0x02;
|
||||
/** For un-indexed collections this will be followed by the number of elements in the values collection, otherwise it will be followed by the number of elements in the operations collection and values collection - one per index/value added. */
|
||||
public static final byte OPERATION_ADD = 0x03;
|
||||
/** For indexed collectons only: The values collection will contain exactly one int[] for the new order mapping. */
|
||||
public static final byte OPERATION_ORDER = 0x04;
|
||||
|
||||
/** The ordered set of operations to be performed by the receiving collection. Some operations are followed by parameters such as a count of values that apply to the operation. */
|
||||
public IntArray operations = null;
|
||||
/** The index into the operations integer collection of the last operation identifier. This is useful for condensing multiple operations of the same type. */
|
||||
public int lastOperationIndex = -1;
|
||||
/** The values, used as specified by the operation and its parameters. */
|
||||
public LiteList values = null;
|
||||
/** Whether the collection is marked as read only. This is only valid in the reflection registration result since the flag is set in the collection when it is created. */
|
||||
public boolean isReadOnly = false;
|
||||
/** Whether the collection values are marked as part of the collection. This is only valid in the reflection registration result since the flag is set in the collection when it is created. */
|
||||
public boolean isPartOf = false;
|
||||
/** A non-serialized reference to a reflection holder. This is used to simplify passing the data and holder outside the synch block before creating reflections. */
|
||||
public ReflectionHolder holder = null;
|
||||
/**
|
||||
* ReflectCollectionData constructor.
|
||||
*/
|
||||
public ReflectCollectionData() {
|
||||
super();
|
||||
}//ReflectCollectionData()//
|
||||
/**
|
||||
* Determines whether the data set has changes or is empty and not worth sending to any reflections or reflected objects.
|
||||
* @return Whether there is significant data in this set.
|
||||
*/
|
||||
public boolean hasChanges() {
|
||||
return operations != null && operations.getSize() > 0;
|
||||
}//hasChanges()//
|
||||
/**
|
||||
* Adds an ADD operation to the data set.
|
||||
* <p>A collection should only ever use indexed adds or non-indexed adds, these should never be mixed!</p>
|
||||
* @param addedValue The value being added.
|
||||
* @param index The index (this is the index after all previous operations have been applied) of the value (range checking not performed).
|
||||
*/
|
||||
public void addAddOperation(Object addedValue, int index) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_ADD) {
|
||||
operations.ensureCapacity(1);
|
||||
values.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + 1);
|
||||
operations.add(index);
|
||||
values.add(addedValue);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(3, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(3);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_ADD);
|
||||
operations.add(1);
|
||||
operations.add(index);
|
||||
values.add(addedValue);
|
||||
}//else//
|
||||
}//addAddOperation()//
|
||||
/**
|
||||
* Adds an ADD operation to the data set.
|
||||
* <p>A collection should only ever use indexed adds or non-indexed adds, these should never be mixed!</p>
|
||||
* @param addedValues The values being added.
|
||||
* @param indices The indices (the index at N+1 should be valid only after the add of the value at index N is applied) of the values (range checking not performed, the collections of indices and added values must be identically sized - this check is not performed).
|
||||
*/
|
||||
public void addAddOperation(ICollection addedValues, IntArray indices) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_ADD) {
|
||||
operations.ensureCapacity(indices.getSize());
|
||||
values.ensureCapacity(addedValues.getSize());
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + indices.getSize());
|
||||
operations.addAll(indices);
|
||||
values.addAll(addedValues);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(indices.getSize() + 2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(indices.getSize() + 2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(addedValues.getSize(), 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(addedValues.getSize());
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_ADD);
|
||||
operations.add(indices.getSize());
|
||||
operations.addAll(indices);
|
||||
values.addAll(addedValues);
|
||||
}//else//
|
||||
}//addAddOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param index The index of the value removed (this is the index after all previous operations have been applied).
|
||||
*/
|
||||
public void addRemoveOperation(int index) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
operations.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + 1);
|
||||
operations.add(index);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(3, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(3);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(1);
|
||||
operations.add(index);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param removedValue The value removed.
|
||||
* @param index The index of the value removed (this is the index after all previous operations have been applied).
|
||||
*/
|
||||
public void addRemoveOperation(Object removedValue, int index) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
operations.ensureCapacity(1);
|
||||
values.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + 1);
|
||||
operations.add(index);
|
||||
values.add(removedValue);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(3, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(3);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(1);
|
||||
operations.add(index);
|
||||
values.add(removedValue);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param indices The indices of the values removed (the index at N+1 should be valid only after the remove of the value at index N is applied).
|
||||
*/
|
||||
public void addRemoveOperation(IntArray indices) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
operations.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + indices.getSize());
|
||||
operations.addAll(indices);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(indices.getSize() + 2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(indices.getSize() + 2);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(indices.getSize());
|
||||
operations.addAll(indices);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param removedValues The values that have been removed from the collection.
|
||||
* @param indices The indices of the values removed (the index at N+1 should be valid only after the remove of the value at index N is applied).
|
||||
*/
|
||||
public void addRemoveOperation(ICollection removedValues, IntArray indices) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
operations.ensureCapacity(indices.getSize());
|
||||
values.ensureCapacity(removedValues.getSize());
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + indices.getSize());
|
||||
operations.addAll(indices);
|
||||
values.addAll(removedValues);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(indices.getSize() + 2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(indices.getSize() + 2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(removedValues.getSize(), 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(removedValues.getSize());
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(indices.getSize());
|
||||
operations.addAll(indices);
|
||||
values.addAll(removedValues);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a ORDER operation to the data set.
|
||||
* @param mapping The mapping between the current indices and the new indices for all values in the collection (as of the application of the previous operation).
|
||||
*/
|
||||
public void addOrderOperation(int[] mapping) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_ORDER) {
|
||||
values.replace(values.getSize() - 1, mapping);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_ORDER);
|
||||
values.add(mapping);
|
||||
}//else//
|
||||
}//addOrderOperation()//
|
||||
/**
|
||||
* Adds an ADD operation to the data set.
|
||||
* <p>A collection should only ever use indexed adds or non-indexed adds, these should never be mixed!</p>
|
||||
* @param addedValue The value being added.
|
||||
*/
|
||||
public void addAddOperation(Object addedValue) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_ADD) {
|
||||
operations.ensureCapacity(1);
|
||||
values.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + 1);
|
||||
values.add(addedValue);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_ADD);
|
||||
operations.add(1);
|
||||
values.add(addedValue);
|
||||
}//else//
|
||||
}//addAddOperation()//
|
||||
/**
|
||||
* Adds an ADD operation to the data set.
|
||||
* <p>A collection should only ever use indexed adds or non-indexed adds, these should never be mixed!</p>
|
||||
* @param addedValues The values being added.
|
||||
*/
|
||||
public void addAddOperation(ICollection addedValues) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_ADD) {
|
||||
values.ensureCapacity(addedValues.getSize());
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + addedValues.getSize());
|
||||
values.addAll(addedValues);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(addedValues.getSize(), 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(addedValues.getSize());
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_ADD);
|
||||
operations.add(addedValues.getSize());
|
||||
values.addAll(addedValues);
|
||||
}//else//
|
||||
}//addAddOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param removedValue The value removed.
|
||||
*/
|
||||
public void addRemoveOperation(Object removedValue) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
values.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + 1);
|
||||
values.add(removedValue);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(1);
|
||||
values.add(removedValue);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a REMOVE operation to the data set.
|
||||
* <p>A collection should only ever use indexed removes or non-indexed removes, these should never be mixed!</p>
|
||||
* @param removedValues The values removed.
|
||||
*/
|
||||
public void addRemoveOperation(ICollection removedValues) {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE) {
|
||||
values.ensureCapacity(1);
|
||||
operations.replace(lastOperationIndex + 1, operations.get(lastOperationIndex + 1) + removedValues.getSize());
|
||||
values.addAll(removedValues);
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(2, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(2);
|
||||
}//else//
|
||||
|
||||
if(values == null) {
|
||||
values = new LiteList(removedValues.getSize(), 100);
|
||||
}//if//
|
||||
else {
|
||||
values.ensureCapacity(removedValues.getSize());
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE);
|
||||
operations.add(removedValues.getSize());
|
||||
values.addAll(removedValues);
|
||||
}//else//
|
||||
}//addRemoveOperation()//
|
||||
/**
|
||||
* Adds a REMOVE_ALL operation to the data set.
|
||||
*/
|
||||
public void addRemoveAllOperation() {
|
||||
if(lastOperationIndex != -1 && operations.get(lastOperationIndex) == OPERATION_REMOVE_ALL) {
|
||||
//Do nothing.//
|
||||
}//if//
|
||||
else {
|
||||
if(operations == null) {
|
||||
operations = new IntArray(1, 100);
|
||||
}//if//
|
||||
else {
|
||||
operations.ensureCapacity(1);
|
||||
}//else//
|
||||
|
||||
lastOperationIndex = operations.getSize();
|
||||
operations.add(OPERATION_REMOVE_ALL);
|
||||
}//else//
|
||||
}//addRemoveAllOperation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectData#createReflection()
|
||||
*/
|
||||
public IReflectable createReflection() throws InstantiationException, IllegalAccessException {
|
||||
return ReflectCollectionSupport.createReflection(this);
|
||||
}//createReflection()//
|
||||
/**
|
||||
* 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 {
|
||||
super.readExternal(in);
|
||||
short options = in.readByte();
|
||||
|
||||
isReadOnly = (options & OPTION_READ_ONLY) != 0;
|
||||
isPartOf = (options & OPTION_PART_OF) != 0;
|
||||
operations = (IntArray) in.readObject();
|
||||
values = (LiteList) in.readObject();
|
||||
}//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 {
|
||||
super.writeExternal(out);
|
||||
|
||||
out.writeByte((byte) ((isReadOnly ? OPTION_READ_ONLY : 0) | (isPartOf ? OPTION_PART_OF : 0)));
|
||||
out.writeObject(operations);
|
||||
out.writeObject(values);
|
||||
}//readExternal()//
|
||||
}//ReflectCollectionData//
|
||||
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.common.orb.*;
|
||||
import com.common.util.*;
|
||||
import com.common.util.optimized.IntArray;
|
||||
import com.common.debug.*;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.common.MetadataContainer;
|
||||
import com.foundation.exception.ReflectionSynchronizationException;
|
||||
import com.foundation.transaction.Transaction;
|
||||
import com.foundation.transaction.TransactionContextHolder;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.util.*;
|
||||
|
||||
/**
|
||||
* Supports a collection capable of being & having a reflection.
|
||||
* This class provides common functionality for collections that implement IReflactableCollection.
|
||||
*/
|
||||
public abstract class ReflectCollectionSupport extends AbstractReflectSupport implements IReflectCollectionSupport {
|
||||
/** The object that is being reflected. This should be null if this object is supporting the original object. */
|
||||
private IReflectableCollection reflectedObject = null;
|
||||
/** This will be non-null while changes are being synchronized to this reflected collection. The changes that are applied will be collected in this object and the synchronize code will send this event to registered reflections after all changes have been applied. */
|
||||
private ReflectCollectionData eventData = null;
|
||||
/**
|
||||
* ReflectCollectionSupport constructor.
|
||||
*/
|
||||
protected ReflectCollectionSupport() {
|
||||
super();
|
||||
}//ReflectCollectionSupport()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
protected Object clone() {
|
||||
ReflectCollectionSupport clone = null;
|
||||
|
||||
try {
|
||||
clone = (ReflectCollectionSupport) super.clone();
|
||||
}//try//
|
||||
catch(CloneNotSupportedException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
//None of the attributes should be copied.//
|
||||
clone.reflectedObject = null;
|
||||
clone.eventData = null;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/**
|
||||
* Gets the event data used to update reflections after another reflection synchronizes with this reflected object.
|
||||
* @return The data container for the post synchronize update to all registered reflections of this object.
|
||||
*/
|
||||
protected ReflectCollectionData getEventData() {
|
||||
return eventData;
|
||||
}//getEventData()//
|
||||
/**
|
||||
* Creates a reflection given a reflectable object.
|
||||
* A reflection can be used to create a local copy that gets updated and can delay changes to the reflected object.
|
||||
* @param reflected The object to be reflected.
|
||||
* @return The reflection of the reflected object.
|
||||
* @see IReflectableCollection.TYPE_DEFAULT
|
||||
*/
|
||||
public static IReflectable createReflection(ReflectCollectionData reflectionData) throws InstantiationException, IllegalAccessException {
|
||||
IReflectable reflection = null;
|
||||
IReflectable replacementReflection = null;
|
||||
|
||||
//Create a reflection object of the specified type.//
|
||||
//TODO: It would be nice to have the original collection pass initialization parameters so that the correct constructor can be called with some reasonable parameters.
|
||||
try {
|
||||
if(reflectionData.reflectedTypeConstructorParameters != null && reflectionData.reflectedTypeConstructorSignature != null) {
|
||||
reflection = (IReflectable) reflectionData.reflectedType.getConstructor(reflectionData.reflectedTypeConstructorSignature).newInstance(reflectionData.reflectedTypeConstructorParameters);
|
||||
}//if//
|
||||
else {
|
||||
reflection = (IReflectable) reflectionData.reflectedType.newInstance();
|
||||
}//else//
|
||||
}//try//
|
||||
catch(NoSuchMethodException e) {
|
||||
Debug.log(e);
|
||||
//Attempt to use the default constructor and keep the app running.//
|
||||
reflection = (IReflectable) reflectionData.reflectedType.newInstance();
|
||||
}//catch//
|
||||
catch(InvocationTargetException e) {
|
||||
Debug.log(e);
|
||||
//Attempt to use the default constructor and keep the app running.//
|
||||
reflection = (IReflectable) reflectionData.reflectedType.newInstance();
|
||||
}//catch//
|
||||
|
||||
//Set the reflection's initial state.//
|
||||
replacementReflection = (IReflectable) reflection.zzrReflectionInitialize(reflection, reflectionData); //The reflection will call its reflect support's initializeReflection(CollectionReflectionData) method.//
|
||||
|
||||
if(replacementReflection != null) {
|
||||
reflection.zzrReflectionDestroy(reflection);
|
||||
}//if//
|
||||
|
||||
return replacementReflection != null ? replacementReflection : reflection;
|
||||
}//createReflection()//
|
||||
/**
|
||||
* Prepares a reflection data object containing all of the added values for a given reflection.
|
||||
* <p>Note: This is only to be used when creating a reflection of the collection, not when updating a reflection.</p>
|
||||
* @param reflectionData The container for the data that holds the added values that may be reflected.
|
||||
* @param createReflectDataContext The context for the process of creating reflections of IReflectable references.
|
||||
*/
|
||||
protected void prepareReflectionData(ReflectCollectionData reflectionData, CreateReflectDataContext createReflectDataContext) {
|
||||
boolean isRemote = !Orb.isLocal(createReflectDataContext.getUpdateHandler());
|
||||
|
||||
if(reflectionData.values != null && reflectionData.values.getSize() > 0) {
|
||||
for(int index = 0, length = reflectionData.values.getSize(); index < length; index++) {
|
||||
Object value = reflectionData.values.get(index);
|
||||
|
||||
if(value instanceof IReflectable) {
|
||||
value = isRemote ? Orb.getProxy(value, IReflectable.class) : value;
|
||||
createReflectDataContext.include((IReflectable) value);
|
||||
|
||||
if(isPartOf() && createReflectDataContext.isSynchronizationResult()) {
|
||||
((IReflectable) value).zzrCollectPostSynchronizeReflectables(value, createReflectDataContext);
|
||||
}//if//
|
||||
}//else if//
|
||||
else {
|
||||
//TODO: When we have metadata for the reflection generation, we should perhaps pass it to this clone method which can pass it to ICloneable values.
|
||||
value = isRemote ? value : cloneValue(value);
|
||||
}//else//
|
||||
|
||||
reflectionData.values.set(index, value);
|
||||
}//for//
|
||||
}//if//
|
||||
}//prepareReflectionData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getReflectedObject()
|
||||
*/
|
||||
public IReflectable getReflectedObject() {
|
||||
return reflectedObject != null ? reflectedObject : (IReflectable) this;
|
||||
}//getReflectedObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getReflectionObject()
|
||||
*/
|
||||
public IReflectable getReflectionObject() {
|
||||
return (IReflectableCollection) getSupportedCollection();
|
||||
}//getReflectionObject()//
|
||||
/**
|
||||
* Gets the supported collection.
|
||||
* @return The collection supported by this reflect support object.
|
||||
*/
|
||||
protected abstract ICollection getSupportedCollection();
|
||||
/**
|
||||
* 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 abstract boolean isPartOf();
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getSupportedObject()
|
||||
*/
|
||||
public IReflectable getSupportedObject() {
|
||||
return (IReflectable) getSupportedCollection();
|
||||
}//getSupportedObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#collectReflectUpdateHandlers(com.common.util.LiteHashMap)
|
||||
*/
|
||||
protected void collectReflectUpdateHandlers(LiteHashMap reflectedMap) {
|
||||
//TODO: Ensure this goes to the AbstractReflectSupport's method and not an overload of the method.
|
||||
super.internalCollectReflectUpdateHandlers(reflectedMap);
|
||||
}//collectReflectUpdateHandlers()//
|
||||
/**
|
||||
* Initializes this object as a reflection of another object of the same type.
|
||||
* @param reflectionData The data necessary to initialize the object as a reflection of another object.
|
||||
* @return A replacement reflection if another reflection exists for the reflected object, or null if the initialization went smoothly.
|
||||
*/
|
||||
protected Object reflectionInitialize(ReflectCollectionData reflectionData) {
|
||||
//Save the reflected reference.//
|
||||
reflectedObject = (IReflectableCollection) ((ReflectCollectionData) reflectionData).reflected;
|
||||
|
||||
return super.reflectionInitialize(reflectionData);
|
||||
}//initializeReflection()//
|
||||
/**
|
||||
* Called after the initializeReflection method and after all reflections in a bundle have been initialized.
|
||||
* Allows the reflection to replace reflected references with their reflections.
|
||||
* @param reflectionData The data necessary to initialize the reflection.
|
||||
*/
|
||||
protected abstract void reflectionPostInitialize(ReflectCollectionData reflectionData);
|
||||
/**
|
||||
* Adds a value to the supported collection without marking the added value as being a change.
|
||||
* This method is only called when the supported collection is a reflection and the reflected collection sends updates.
|
||||
* @param value The value added to the collection.
|
||||
* @param recordChanges Whether the changes should be recorded by the collection (trusted change versus untrusted).
|
||||
*/
|
||||
protected abstract void reflectSupportAddValue(Object value, boolean recordChanges, boolean ignoreContainment);
|
||||
/**
|
||||
* Adds values to the supported collection without marking the added values as being a change.
|
||||
* This method is only called when the supported collection is a reflection and the reflected collection sends updates.
|
||||
* @param values The values added to the collection.
|
||||
* @param recordChanges Whether the changes should be recorded by the collection (trusted change versus untrusted).
|
||||
*/
|
||||
protected abstract void reflectSupportAddValues(ICollection values, boolean recordChanges);
|
||||
/**
|
||||
* Releases all suppressed events so that they will be fired as one event.
|
||||
* @see #reflectSupportSuppressEvents()
|
||||
*/
|
||||
protected abstract void reflectSupportReleaseEvents();
|
||||
/**
|
||||
* Removes a value from the supported collection without marking the removed value as being a change.
|
||||
* This method is only called when the supported collection is a reflection and the reflected collection sends updates.
|
||||
* @param value The value removed from the collection.
|
||||
* @param recordChanges Whether the changes should be recorded by the collection (trusted change versus untrusted).
|
||||
*/
|
||||
protected abstract void reflectSupportRemoveValue(Object value, boolean recordChanges, boolean ignoreContainment);
|
||||
/**
|
||||
* Removes values from the supported collection without marking the removed values as being a change.
|
||||
* This method is only called when the supported collection is a reflection and the reflected collection sends updates.
|
||||
* @param values The values removed from the collection.
|
||||
* @param recordChanges Whether the changes should be recorded by the collection (trusted change versus untrusted).
|
||||
*/
|
||||
protected abstract void reflectSupportRemoveValues(ICollection values, boolean recordChanges);
|
||||
/**
|
||||
* Resets the collections of added and removed values.
|
||||
*/
|
||||
protected abstract void reflectSupportResetChangeTracking();
|
||||
/**
|
||||
* Suppresses all events so that they will be fired as one event at a later time.
|
||||
*/
|
||||
protected abstract void reflectSupportSuppressEvents();
|
||||
/**
|
||||
* Registers a new reflection of the collection.
|
||||
* @param reflectDataContext The context for the creation of this object's reflection.
|
||||
*/
|
||||
protected void registerReflection(CreateReflectDataContext createReflectDataContext) {
|
||||
IReflectUpdateHandler updateHandler = createReflectDataContext.getUpdateHandler();
|
||||
|
||||
if(Orb.isProxy(updateHandler) && Orb.isLocal(updateHandler)) {
|
||||
updateHandler = (IReflectUpdateHandler) Orb.getLocal(updateHandler);
|
||||
}//if//
|
||||
|
||||
//Don't collect reflection data for objects that are already reflected in the given reflection context (represented by the update handler).//
|
||||
if(!isReflected(updateHandler)) {
|
||||
ReflectCollectionData reflectionData = null;
|
||||
|
||||
try {
|
||||
ReflectionHolder reflectionHolder = null;
|
||||
long reflectionId = createReflectDataContext.getIdHolder().generateId();
|
||||
|
||||
//Setup the reflection holder and add it to the collection of registered reflections.//
|
||||
reflectionHolder = new ReflectionHolder(updateHandler, reflectionId, createReflectDataContext.getMetadataContainer());
|
||||
setRegisteredReflection(reflectionHolder);
|
||||
reflectionHolder.registerDisconnectHandler((IReflectable) getSupportedObject());
|
||||
|
||||
//Initialize the reflection data object containing all of the values in the reflection.//
|
||||
reflectionData = createReflectCollectionData();
|
||||
reflectionData.holder = reflectionHolder;
|
||||
reflectionData.reflectionId = reflectionId;
|
||||
reflectionData.reflected = Orb.isLocal(createReflectDataContext.getUpdateHandler()) ? (IReflectable) this : (IReflectable) Orb.getProxy(this, IReflectableCollection.class);
|
||||
reflectionData.reflectedType = getSupportedCollection().getClass();
|
||||
setupReflectionDataConstructor(reflectionData);
|
||||
reflectionData.mappedReflectionId = initializeUpdateMessageNumber(updateHandler, reflectionId);
|
||||
initializeReflectCollectionData(reflectionData);
|
||||
|
||||
//Add the reflection data to the container.//
|
||||
createReflectDataContext.getReflectionDataContainer().add(reflectionData);
|
||||
//Prepare the reflection data by proxing and generating reflections and cloning as necessary.//
|
||||
prepareReflectionData(reflectionData, createReflectDataContext);
|
||||
reflectionData.includesReferencedData(true);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Reflection data collection failed.", e);
|
||||
reflectionData = null;
|
||||
}//catch//
|
||||
}//if//
|
||||
}//registerReflection()//
|
||||
/**
|
||||
* Sets up the specific collection class parameters for instantiating a new collection that will be used as a reflection of the original.
|
||||
* @param reflectionData The reflection data that must be initialized with the signature and parameter values for creating a new reflection of the collection.
|
||||
*/
|
||||
protected void setupReflectionDataConstructor(ReflectCollectionData reflectionData) {
|
||||
//Should be overloaded by each instantiatable collection class to provide the reflectionData.reflectedTypeConstructorSignature and reflectionData.reflectedTypeConstructorParameters values.//
|
||||
//reflectionData.reflectedTypeConstructorSignature = new Class[] {...}
|
||||
//reflectionData.reflectedTypeConstructorParameters = new Object[] {...}
|
||||
}//setupReflectionDataConstructor()//
|
||||
/**
|
||||
* Initializes the reflect collection data. This method provides subclasses an opportunity to add data.
|
||||
* @param reflectionData The reflection data container for this reflected collection. This may be a subclass whose type is defined by the createReflectCollectionData() method.
|
||||
* @see #createReflectCollectionData()
|
||||
*/
|
||||
protected void initializeReflectCollectionData(ReflectCollectionData reflectionData) {
|
||||
reflectionData.addAddOperation(new LiteList((ICollection) getSupportedCollection()));
|
||||
}//registerReflection()//
|
||||
/**
|
||||
* Notifies reflections when this reflected collection is modified.
|
||||
* @param data The change data that identifies the added and removed values for the reflections.
|
||||
* @param ignoredUpdateHandler The handler that should be ignored, or null if all handlers should receive the data.
|
||||
* @return The reflection collection data for the ignored update handler. This will be null if the ignored update handler is null.
|
||||
*/
|
||||
protected ReflectCollectionData sendUpdate(ReflectCollectionData data, IReflectUpdateHandler ignoredUpdateHandler) {
|
||||
ReflectCollectionData result = null;
|
||||
|
||||
if(hasReflections()) {
|
||||
IIterator iterator = getRegisteredReflections();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
ReflectionHolder reflectionHolder = (ReflectionHolder) iterator.next();
|
||||
|
||||
if(Orb.checkExactEquality(reflectionHolder.getHandler(), ignoredUpdateHandler)) {
|
||||
result = data;
|
||||
result.holder = reflectionHolder;
|
||||
}//if//
|
||||
else {
|
||||
ReflectCollectionData reflectionData = createReflectCollectionData();
|
||||
boolean isRemote = !reflectionHolder.isLocal(); //Whether the reflection is remote or local.//
|
||||
|
||||
//TODO: This code must create reflections of the data as necessary.
|
||||
//TODO: The method should be able to get the metadata for creating reflections from the reflection holder.
|
||||
//TODO: The method will either have to be passed a CreateReflectDataContext or will have to create one.
|
||||
|
||||
reflectionData.reflectionId = reflectionHolder.getReflectionId(); //This identifies who is sending the message through the handler.//
|
||||
reflectionData.reflectedType = getSupportedCollection().getClass(); //Is this always necessary? This may only be necessary if we are creating a new reflection.//
|
||||
reflectionData.reflected = null;
|
||||
reflectionData.operations = data.operations == null ? null : (IntArray) data.operations.clone();
|
||||
reflectionData.values = data.values == null ? null : (LiteList) data.values.clone();
|
||||
|
||||
//Prepare the values for sending.//
|
||||
for(int index = 0, length = reflectionData.values == null ? 0 : reflectionData.values.getSize(); index < length; index++) {
|
||||
Object value = reflectionData.values.get(index);
|
||||
|
||||
if(value instanceof IReflectableObject) {
|
||||
value = isRemote ? Orb.getProxy(value, IReflectableObject.class) : value;
|
||||
}//else if//
|
||||
else {
|
||||
//TODO: When we have metadata for the reflection generation, we should perhaps pass it to this clone method which can pass it to ICloneable values.
|
||||
value = isRemote ? value : cloneValue(value);
|
||||
}//else//
|
||||
|
||||
reflectionData.values.set(index, value);
|
||||
}//for//
|
||||
|
||||
try {
|
||||
//Update the holder using a customized reflection data object. It must be customized because the adds and removes must take into account the reflection type and remoteness of the reflection.//
|
||||
updateReflection(reflectionHolder, reflectionData);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
if(Orb.isInvalidProxyException(e)) {
|
||||
//Do nothing. The reflection should be unregistered automatically.//
|
||||
}//if//
|
||||
else {
|
||||
Debug.log(e);
|
||||
}//else//
|
||||
}//catch//
|
||||
}//else//
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//sendUpdate()//
|
||||
/**
|
||||
* Creates a new collection data for this reflected collection or reflection of a collection.
|
||||
* @return The data object for this collection type.
|
||||
*/
|
||||
protected ReflectCollectionData createReflectCollectionData() {
|
||||
return new ReflectCollectionData();
|
||||
}//createReflectCollectionData()//
|
||||
/**
|
||||
* Synchronizes changes from a refection of this object.
|
||||
* @param updateHandler The handler used to identify the reflection that is performing the synchronization of data. The synchronizing reflection will only receive notification of the changes through this method's return value.
|
||||
* @param abstractData The collection of added and removed values.
|
||||
* @return The collection of changes actually applied to the collection.
|
||||
*/
|
||||
public final ReflectCollectionData synchronizeReflection(IReflectUpdateHandler updateHandler, AbstractReflectData abstractData) {
|
||||
ReflectCollectionData result = null;
|
||||
TransactionContextHolder contextHolder = null;
|
||||
|
||||
try {
|
||||
contextHolder = TransactionContextHolder.getInstance(null);
|
||||
//Setup a data container to which the changes will be added so that after synchronizing we can update all reflections.//
|
||||
eventData = createReflectCollectionData();
|
||||
//Give subclasses an opportunity to prepare for a synchronize event. For example, ordered lists may want to store some ordering info prior to being updated so that the new ordering can be sent out after the changes are applied.//
|
||||
preReflectionSynchronization();
|
||||
|
||||
try {
|
||||
//Perform the synchronization which will change this reflected object.//
|
||||
internalSynchronizeReflection(updateHandler, (ReflectCollectionData) abstractData);
|
||||
}//try//
|
||||
finally {
|
||||
//Notify the subclasses that the synchronization has completed.//
|
||||
postReflectionSynchronization();
|
||||
}//finally//
|
||||
|
||||
//Commit any changes to the collection.//
|
||||
if(!contextHolder.commit()) {
|
||||
if(!contextHolder.rollback()) {
|
||||
Debug.log(new RuntimeException("Error: Failed to commit, and then rollback the transaction. The repository may be in an unexpected state (repository specific behavior unknown)."));
|
||||
}//if//
|
||||
|
||||
throw new ReflectionSynchronizationException(new TransactionErrorInfo(Transaction.ERROR_COMMIT_FAILED, IEntity.MODEL_ERROR_NONE));
|
||||
}//if//
|
||||
|
||||
//If there were any significant changes applied to this reflected object then update the reflections.//
|
||||
if(getEventData().hasChanges()) {
|
||||
result = sendUpdate(getEventData(), updateHandler);
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
eventData = null;
|
||||
|
||||
if(contextHolder != null) {
|
||||
contextHolder.close();
|
||||
contextHolder = null;
|
||||
}//if//
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//synchronizeReflection()//
|
||||
/**
|
||||
* Called prior to synchronizing a reflection with this reflected object (changes being applied to this object).
|
||||
*/
|
||||
protected void preReflectionSynchronization() {
|
||||
}//preReflectionSynchronization()//
|
||||
/**
|
||||
* Called after synchronizing a reflection with this reflected object (changes being applied to this object).
|
||||
*/
|
||||
protected void postReflectionSynchronization() {
|
||||
}//postReflectionSynchronization()//
|
||||
/**
|
||||
* Called prior to updating a reflection with changes made by the reflected object (changes being applied to this object).
|
||||
*/
|
||||
protected void preReflectionUpdate() {
|
||||
}//preReflectionUpdate()//
|
||||
/**
|
||||
* Called after updating a reflection with changes made by the reflected object (changes being applied to this object).
|
||||
*/
|
||||
protected void postReflectionUpdate() {
|
||||
}//postReflectionUpdate()//
|
||||
/**
|
||||
* Synchronizes changes from a refection of this object. This internal method allows easy overloading for subclasses.
|
||||
* @param updateHandler The handler used to identify the reflection that is performing the synchronization of data. The synchronizing reflection will only receive notification of the changes through this method's return value.
|
||||
* @param data The collection of added and removed values.
|
||||
*/
|
||||
protected abstract void internalSynchronizeReflection(IReflectUpdateHandler updateHandler, ReflectCollectionData data);
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#updateReflection(com.foundation.attribute.AbstractReflectData)
|
||||
*/
|
||||
public void updateReflection(AbstractReflectData abstractData) {
|
||||
ReflectCollectionData data = (ReflectCollectionData) abstractData;
|
||||
|
||||
try {
|
||||
if(hasReflections()) {
|
||||
//Set the event data to collect changes to be sent to other reflections of this reflection.//
|
||||
eventData = createReflectCollectionData();
|
||||
}//if//
|
||||
|
||||
//TODO: It might be nice to move this to this method's caller so that queued messages get processed as one unit from the perspective of the reflection.
|
||||
preReflectionUpdate();
|
||||
|
||||
try {
|
||||
internalUpdateReflection(data);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Reflection update failed.", e);
|
||||
}//catch//
|
||||
|
||||
postReflectionUpdate();
|
||||
|
||||
//If there were any significant changes applied to this reflected object then update the reflections.//
|
||||
if((hasReflections()) && (getEventData().hasChanges())) {
|
||||
sendUpdate(getEventData(), null);
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
eventData = null;
|
||||
}//finally//
|
||||
}//updateReflection()//
|
||||
/**
|
||||
* Performs the actual update of the collection reflection using the given change data.
|
||||
* The reflected object is sending a set of changes to this reflection.
|
||||
* @param data The set of changes to be applied to this collection.
|
||||
*/
|
||||
protected abstract void internalUpdateReflection(ReflectCollectionData data);
|
||||
}//ReflectCollectionSupport//
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/**
|
||||
* Contains a set of reflection data generated by a reflected object in its call to reflectionRegister(..).
|
||||
* Also passes the last reflection process identifier used to generate the reflection datas on remote processes without making calls to the reflection context to get identifiers.
|
||||
*/
|
||||
public class ReflectDataContainer implements java.io.Externalizable {
|
||||
/** The collection of AbstractReflectData objects that make up the reflection data that were generated. */
|
||||
private LiteList reflectionData = null;
|
||||
/**
|
||||
* ReflectDataContainer constructor.
|
||||
* For serialization use only.
|
||||
*/
|
||||
public ReflectDataContainer() {
|
||||
super();
|
||||
}//ReflectDataContainer()//
|
||||
/**
|
||||
* ReflectDataContainer constructor.
|
||||
* @param reflectionData The collection of AbstractReflectData objects that make up the reflection data that were generated.
|
||||
*/
|
||||
public ReflectDataContainer(LiteList reflectionData) {
|
||||
super();
|
||||
this.reflectionData = reflectionData;
|
||||
}//ReflectDataContainer()//
|
||||
/**
|
||||
* Gets the collection of AbstractReflectData objects that make up the reflection data that were generated.
|
||||
* @return The set of reflection data objects for the objects that were reflected.
|
||||
*/
|
||||
public LiteList getReflectionData() {
|
||||
return reflectionData;
|
||||
}//getReflectionData()//
|
||||
/**
|
||||
* 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 {
|
||||
reflectionData = (LiteList) in.readObject();
|
||||
}//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 {
|
||||
out.writeObject(reflectionData);
|
||||
}//readExternal()//
|
||||
}//ReflectDataContainer//
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.util.optimized.IntArray;
|
||||
|
||||
public class ReflectIndexedCollectionData extends ReflectCollectionData {
|
||||
/** Whether the collection should retain its ordering information (and consiquentially not allow an order comparator). */
|
||||
public boolean retainOrdering = false;
|
||||
/**
|
||||
* ReflectIndexedCollectionData constructor.
|
||||
*/
|
||||
public ReflectIndexedCollectionData() {
|
||||
super();
|
||||
}//ReflectIndexedCollectionData()//
|
||||
/**
|
||||
* 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 {
|
||||
super.readExternal(in);
|
||||
retainOrdering = in.readBoolean();
|
||||
}//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 {
|
||||
super.writeExternal(out);
|
||||
out.writeBoolean(retainOrdering);
|
||||
}//readExternal()//
|
||||
}//ReflectIndexedCollectionData//
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
public class ReflectObjectData extends AbstractReflectData {
|
||||
/** The collection of the reflected object's attributes. */
|
||||
public AttributeSupport.AttributeCollection data = null;
|
||||
/**
|
||||
* ReflectObjectData constructor.
|
||||
*/
|
||||
public ReflectObjectData() {
|
||||
super();
|
||||
}//ReflectObjectData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectData#createReflection()
|
||||
*/
|
||||
public IReflectable createReflection() throws InstantiationException, IllegalAccessException {
|
||||
return ReflectObjectSupport.createReflection(this);
|
||||
}//createReflection()//
|
||||
/**
|
||||
* 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 {
|
||||
super.readExternal(in);
|
||||
/*byte version = */in.readByte();
|
||||
data = (AttributeSupport.AttributeCollection) in.readObject();
|
||||
}//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 {
|
||||
super.writeExternal(out);
|
||||
out.writeByte(0);
|
||||
out.writeObject(data);
|
||||
}//readExternal()//
|
||||
}//ReflectObjectData//
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.orb.*;
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
import com.foundation.attribute.AttributeSupport;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.metadata.CloneContext;
|
||||
|
||||
/*
|
||||
* Supports an entity by allowing for local and remote registered clones or copies of the entity. The clones can be modified without affecting the entity until the changes are synchronized.
|
||||
* The application should obtain a lock (application dependant) to prevent two user threads from trying to synchronize changes at the same time. Normally this should be done when a view is
|
||||
* opened that could change some entities. All other user threads should be prevented from changing those entities until the view is closed and any changes are applied or discarded. At the
|
||||
* very least the user thread should prevent other user threads from synchronizing changes why it is synchronizing changes. Failure to do this will allow for inconsistent clone states.
|
||||
*/
|
||||
public abstract class ReflectObjectSupport extends AbstractReflectSupport {
|
||||
/** The object supported by this reflectable support. */
|
||||
private IReflectableObject supportedObject = null;
|
||||
/** The object that is being reflected. This should be null if this object is supporting the original object. */
|
||||
private IReflectableObject reflectedObject = null;
|
||||
/** The reflected object's attribute support reference. */
|
||||
private AttributeSupport supportedObjectAttributeSupport = null;
|
||||
/** The model mapping number used only when synchronizing a non-reflection such that the reflection data created upon a successful synchronize passes the number back to the synchronizing reflection context so that it may reuse its copy of the model for the new reflecton, thus preserving any reflections of it. */
|
||||
private int modelMappingNumber = -1;
|
||||
/**
|
||||
* ReflectSupport constructor.
|
||||
*/
|
||||
ReflectObjectSupport() {
|
||||
super();
|
||||
//this.singleThreadedContext = ThreadService.getSingleThreadedContext();
|
||||
}//ReflectSupport()//
|
||||
/**
|
||||
* ReflectSupport constructor.
|
||||
* @param supportedObject The supported object.
|
||||
* @param supportedEntityAttributeSupport The supported object's attribute support object used to update attribute values.
|
||||
* @param supportedObjectEventEmitterSupport The supported object's event emitter support object used to fire attribute changed events.
|
||||
*/
|
||||
ReflectObjectSupport(Object supportedObject) {
|
||||
super(supportedObject);
|
||||
|
||||
//this.singleThreadedContext = ThreadService.getSingleThreadedContext();
|
||||
this.supportedObject = (IReflectableObject) supportedObject;
|
||||
this.supportedObjectAttributeSupport = (AttributeSupport) this;
|
||||
|
||||
if(supportedObject == null) {
|
||||
Debug.log("Error: supported object = null");
|
||||
}//if//
|
||||
}//ReflectSupport()//
|
||||
/**
|
||||
* Sets the model mapping number used only when synchronizing a model that doesn't yet existing in the shared context.
|
||||
* @param modelMappingNumber The number used by the synchronizing reflection context to map the data sent in the synchronize to the models when the synchronized data is reflected at the end of the synchronization.
|
||||
*/
|
||||
public int getModelMappingNumber() {
|
||||
return modelMappingNumber;
|
||||
}//getModelMappingNumber()//
|
||||
/**
|
||||
* Sets the model mapping number used only when synchronizing a model that doesn't yet existing in the shared context.
|
||||
* @param modelMappingNumber The number used by the synchronizing reflection context to map the data sent in the synchronize to the models when the synchronized data is reflected at the end of the synchronization.
|
||||
*/
|
||||
public void setModelMappingNumber(int modelMappingNumber) {
|
||||
this.modelMappingNumber = modelMappingNumber;
|
||||
}//setModelMappingNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#completeSynchronization(com.foundation.attribute.AbstractReflectData, com.foundation.attribute.AbstractReflectData)
|
||||
*/
|
||||
public void completeSynchronization(AbstractReflectData proposedChanges, AbstractReflectData appliedChanges) {
|
||||
AttributeSupport.AttributeCollection attributeCollection = ((ReflectObjectData) appliedChanges).data;
|
||||
|
||||
try {
|
||||
//Note: I commented out the reset of the change flags since it was doing the job of the applyAttributeCollection without firing the proper events, so the apply attribute collection then did nothing.//
|
||||
//Reset the change flags so that when we apply the updates they are not applied to the original values.//
|
||||
//supportedObjectAttributeSupport.resetObjectChangeFlags();
|
||||
//Apply the changes.//
|
||||
supportedObjectAttributeSupport.applyAttributeCollection((AttributeSupport.AttributeCollection) attributeCollection, AttributeSupport.CONTEXT_UNTRUSTED, false);
|
||||
attributeCollection = null;
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Reflection update failed.", e);
|
||||
}//catch//
|
||||
|
||||
//All changes will (currently) always be accepted by the reflected object.//
|
||||
//Only need to reset all change flags.//
|
||||
supportedObjectAttributeSupport.resetObjectChangeFlags();
|
||||
}//completeSynchronization()//
|
||||
/**
|
||||
* Creates a reflection given a reflectable object.
|
||||
* A reflection can be used to create a local copy that gets updated and can delay changes to the reflected object.
|
||||
* @param reflected The object to be reflected.
|
||||
* @return The reflection of the reflected object.
|
||||
*/
|
||||
public static IReflectable createReflection(ReflectObjectData reflectionData) throws InstantiationException, IllegalAccessException {
|
||||
IReflectable reflection = null;
|
||||
IReflectable replacementReflection = null;
|
||||
|
||||
//Create a reflection object of the specified type.//
|
||||
reflection = (IReflectable) reflectionData.reflectedType.newInstance();
|
||||
//Set the reflection's initial state.//
|
||||
replacementReflection = (IReflectable) reflection.zzrReflectionInitialize(reflection, reflectionData); //The reflection will call its reflect support's initializeReflection(ReflectionData) method.//
|
||||
|
||||
//Just in case (should also happen in the ReflectionContext) we will destroy the reflection we were trying to create if another reflection already exists for the given reflected object.//
|
||||
if(replacementReflection != null) {
|
||||
reflection.zzrReflectionDestroy(reflection);
|
||||
}//if//
|
||||
|
||||
return replacementReflection != null ? replacementReflection : reflection;
|
||||
}//createReflection()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#destroyReflection()
|
||||
*/
|
||||
public void destroyReflection() {
|
||||
//Clear the reflected reference.//
|
||||
//reflectedObject = null;
|
||||
|
||||
super.destroyReflection();
|
||||
}//destroyReflection()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getReflectedObject()
|
||||
*/
|
||||
public IReflectable getReflectedObject() {
|
||||
return reflectedObject;
|
||||
}//getReflectedObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getReflectionObject()
|
||||
*/
|
||||
public IReflectable getReflectionObject() {
|
||||
return supportedObject;
|
||||
}//getReflectionObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getSupportedObject()
|
||||
*/
|
||||
public IReflectable getSupportedObject() {
|
||||
return getReflectionObject();
|
||||
}//getSupportedObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#getSynchronizationData()
|
||||
*/
|
||||
public AbstractReflectData getSynchronizationData(CloneContext cloneContext) {
|
||||
ReflectObjectData proposedChanges = null;
|
||||
|
||||
//Check to see if there is something to synchronize to.//
|
||||
if(getReflectionId() != null) {
|
||||
try {
|
||||
AttributeSupport.AttributeCollection attributeCollection = supportedObjectAttributeSupport.getDeltaAttributeCollection(null, true, false); //Note: We want to serialize or clone, not proxy, changes in attributes referencing IReflectable objects.//
|
||||
|
||||
//Return null if the object has not been changed in any significant way.//
|
||||
if(attributeCollection != null) {
|
||||
//TODO: What if we are using a remote single threaded reflection context? Will this still work? If it is remote it won't operate on the same thread. Should we test to see if the destination context shares a thread with this context?
|
||||
ReflectionContext destinationContext = Orb.isLocal(getReflectedObject()) && getReflectedObject().isReflection() ? getReflectedObject().zzrGetReflectionContext(getReflectedObject()) : null;
|
||||
|
||||
//Convert reflections to the reflected references.//
|
||||
attributeCollection.dereflect(getReflectionContext(), destinationContext, supportedObjectAttributeSupport.getTypeMetadata(), cloneContext);
|
||||
//Build our proposed changes object.//
|
||||
proposedChanges = new ReflectObjectData();
|
||||
proposedChanges.data = attributeCollection;
|
||||
}//if//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Dirty reflection data collection failed.", e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
return proposedChanges;
|
||||
}//getSynchronizationData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#initializeReflection(com.foundation.attribute.AbstractReflectData)
|
||||
*/
|
||||
public Object reflectionInitialize(AbstractReflectData reflectionData) {
|
||||
//Save the reflected reference.//
|
||||
reflectedObject = (IReflectableObject) ((ReflectObjectData) reflectionData).reflected;
|
||||
|
||||
return super.reflectionInitialize(reflectionData);
|
||||
}//reflectionInitialize()//
|
||||
/**
|
||||
* Called after initializing this reflection.
|
||||
* Allows the references to reflected objects to be replaced with their reflections.
|
||||
*/
|
||||
public void reflectionPostInitialize(ReflectObjectData reflectionData) {
|
||||
AttributeSupport.AttributeCollection attributeCollection = reflectionData.data;
|
||||
|
||||
//Copy the attribute data into is new reflection.//
|
||||
//Note: Changed this to TRUSTED because with a value of UNTRUSTED it was causing the values to be placed in the altered array, and even worse was forcing the loading of some values from the reflected object (why when we are already applying those values here?).//
|
||||
supportedObjectAttributeSupport.applyAttributeCollection(attributeCollection, AttributeSupport.CONTEXT_TRUSTED, false);
|
||||
}//reflectionPostInitialize()//
|
||||
/**
|
||||
* Gets the attribute number for the given attribute name.
|
||||
* @param attributeName The name of the attribute whose number is to be found.
|
||||
* @return The attribute's number, or -1 if the attribute could not be found.
|
||||
*/
|
||||
protected abstract int getAttributeNumber(String attributeName);
|
||||
/**
|
||||
* Loads the reflection's attribute by requesting the attribute value from the reflected object.
|
||||
* If the result is an AbstractReflectData then the caller should inflate the result into a full reflection before setting the attribute value.
|
||||
* @param attributeName The name of the attribute whose value is desired.
|
||||
* @return Either a serialized value, null if the attribute's value is null, or an instance of AbstractReflectData.
|
||||
*/
|
||||
protected Object loadReflectionAttribute(String attributeName) throws InstantiationException, IllegalAccessException {
|
||||
return getReflectionContext().loadReflectionAttribute((IReflectableObject) getReflectedObject(), attributeName);
|
||||
}//loadReflectionAttribute()//
|
||||
/**
|
||||
* Sets the object this reflect object support is supporting.
|
||||
* @param supportedObject The object being supported.
|
||||
*/
|
||||
public Object loadReflectionAttribute(Attribute attribute, IReflectUpdateHandler updateHandler, ReflectRegistrationData registrationData) {
|
||||
ReflectionHolder reflectionHolder = (ReflectionHolder) getRegisteredReflection(updateHandler);
|
||||
int attributeNumber = attribute.getNumber();
|
||||
Object result = null;
|
||||
|
||||
if(attributeNumber != -1) {
|
||||
result = supportedObjectAttributeSupport.getAttributeValue(attributeNumber);
|
||||
reflectionHolder.getIsAttributeReflected()[attributeNumber] = true;
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//loadReflectionAttribute()//
|
||||
/**
|
||||
* Registers a reflection with the supported reflectable object.
|
||||
* <p>Warning: This code assumes that the reflectable object extends this support class, or it receives the register method invocation and synchronizes before calling this register method.
|
||||
* This class does not specifically synchronize on the supported object.</p>
|
||||
* @param createReflectDataContext The context object for the creation of the reflect data.
|
||||
* @return The last used reflection identifier.
|
||||
*/
|
||||
public void registerReflection(CreateReflectDataContext createReflectDataContext) {
|
||||
IReflectUpdateHandler updateHandler = createReflectDataContext.getUpdateHandler();
|
||||
|
||||
if(Orb.isProxy(updateHandler) && Orb.isLocal(updateHandler)) {
|
||||
updateHandler = (IReflectUpdateHandler) Orb.getLocal(updateHandler);
|
||||
}//if//
|
||||
|
||||
//Don't collect reflection data for objects that are already reflected in the given reflection context (represented by the update handler).//
|
||||
if(!isReflected(updateHandler)) {
|
||||
ReflectObjectData reflectionData = new ReflectObjectData();
|
||||
|
||||
createReflectDataContext.getReflectionDataContainer().add(reflectionData);
|
||||
|
||||
try {
|
||||
AttributeSupport.AttributeCollection attributeCollection = new AttributeSupport.AttributeCollection();
|
||||
boolean[] isAttributeReflected = null;
|
||||
ReflectionHolder reflectionHolder = null;
|
||||
boolean isRemote = !Orb.isLocal(updateHandler);
|
||||
long reflectionId = createReflectDataContext.getIdHolder().generateId();
|
||||
|
||||
//Add the reflection to the collection so that it is updated when attributes change their values.//
|
||||
reflectionHolder = new ReflectionHolder(updateHandler, reflectionId, isAttributeReflected, createReflectDataContext.getMetadataContainer());
|
||||
setRegisteredReflection(reflectionHolder);
|
||||
//Collect the attribute values.//
|
||||
isAttributeReflected = supportedObjectAttributeSupport.createReflectionAttributeCollection(attributeCollection, isRemote, createReflectDataContext);
|
||||
reflectionHolder.setIsAttributeReflected(isAttributeReflected);
|
||||
reflectionHolder.registerDisconnectHandler((IReflectable) getSupportedObject());
|
||||
|
||||
//Setup the reflection data.//
|
||||
reflectionData.includesReferencedData(true);
|
||||
reflectionData.modelMappingNumber = getModelMappingNumber();
|
||||
setModelMappingNumber(-1);
|
||||
reflectionData.reflectionId = reflectionId;
|
||||
reflectionData.reflected = isRemote ? (IReflectable) Orb.getProxy(supportedObject, IReflectableObject.class) : supportedObject;
|
||||
reflectionData.reflectedType = supportedObject.getClass();
|
||||
reflectionData.data = attributeCollection;
|
||||
reflectionData.mappedReflectionId = initializeUpdateMessageNumber(updateHandler, reflectionId);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e, "Reflection data collection failed");
|
||||
reflectionData = null;
|
||||
}//catch//
|
||||
}//if//
|
||||
}//registerReflection()//
|
||||
/**
|
||||
* Sets the object this reflect object support is supporting.
|
||||
* @param supportedObject The object being supported.
|
||||
*/
|
||||
protected void setSupportedObject(IReflectableObject supportedObject) {
|
||||
this.supportedObject = supportedObject;
|
||||
|
||||
super.setSupported(supportedObject);
|
||||
}//setSupportedObject()//
|
||||
/**
|
||||
* Sets the attribute support used by the supported object.
|
||||
* @param supportedObjectAttributeSupport The attribute support object being used by the supported object.
|
||||
*/
|
||||
protected void setSupportedObjectAttributeSupport(AttributeSupport supportedObjectAttributeSupport) {
|
||||
this.supportedObjectAttributeSupport = supportedObjectAttributeSupport;
|
||||
}//setSupportedObjectAttributeSupport()//
|
||||
/**
|
||||
* Synchronizes data from a reflection to this reflected object.
|
||||
* <p>Note: The synchronizing reflection will only receive notification of the changes through this method's return value. An event will not be sent via the update handler.
|
||||
* The returned data will contain the last update message number sent to the reflection. This can be used to ensure that messages are processed in order.</p>
|
||||
* @param updateHandler The handler used to identify the reflection that is performing the synchronization of data.
|
||||
* @param data The set of changed data.
|
||||
* @param createReflectDataContext The context for creating reflections of the IReflectable changes for the synchronizing reflection context.
|
||||
* @return The collection of changes actually applied to the reflected object.
|
||||
*/
|
||||
public AbstractReflectData synchronizeReflection(IReflectUpdateHandler updateHandler, AbstractReflectData reflectData, CreateReflectDataContext createReflectDataContext) {
|
||||
//Update this object with the collection of changed attributes.//
|
||||
//Note: Changed this from TRUSTED to UNTRUSTED because things were not being persisted since it thought nothing had changed after the synchronize.//
|
||||
supportedObjectAttributeSupport.applyAttributeCollection(((ReflectObjectData) reflectData).data, AttributeSupport.CONTEXT_UNTRUSTED, false);
|
||||
//Proxy any reflectable values so that the reflections and the caller can properly create a reflection from the reflectable proxies.//
|
||||
((ReflectObjectData) reflectData).data.proxyValues(IReflectableObject.class);
|
||||
((ReflectObjectData) reflectData).data.proxyValues(IReflectableCollection.class);
|
||||
((ReflectObjectData) reflectData).data.reflectValues(createReflectDataContext, supportedObjectAttributeSupport.getTypeMetadata());
|
||||
reflectData.includesReferencedData(true);
|
||||
//TODO: The updates should be bundled together to be more efficient.
|
||||
//Update any reflections of this reflection.//
|
||||
updateReflections(((ReflectObjectData) reflectData).data, updateHandler);
|
||||
|
||||
return reflectData;
|
||||
}//synchronizeReflection()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.IReflectSupport#updateReflection(com.foundation.attribute.AbstractReflectData)
|
||||
*/
|
||||
public void updateReflection(AbstractReflectData reflectData) {
|
||||
AttributeSupport.AttributeCollection attributeCollection = ((ReflectObjectData) reflectData).data;
|
||||
|
||||
try {
|
||||
//TODO: When the update includes the reflection data for referenced IReflectables we must be certain to apply the changes after creating all the reflections.
|
||||
//Apply the changes.//
|
||||
supportedObjectAttributeSupport.applyAttributeCollection((AttributeSupport.AttributeCollection) attributeCollection, AttributeSupport.CONTEXT_TRUSTED | AttributeSupport.CONTEXT_UPDATE, true);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e, "Reflection update failed.");
|
||||
}//catch//
|
||||
}//updateReflection()//
|
||||
/**
|
||||
* Updates the registered reflections with the specified attribute's value.
|
||||
* @param attributeNumber The number of the attribute whose value should be updated in all reflections.
|
||||
*/
|
||||
public void updateReflections(int attributeNumber) {
|
||||
if(hasReflections()) {
|
||||
//Note: This next method will filter out reflections that don't care.//
|
||||
updateReflections(supportedObjectAttributeSupport.getAttributeCollection(attributeNumber, null), null);
|
||||
}//if//
|
||||
}//updateReflections()//
|
||||
/**
|
||||
* Updates the registered reflections with the collection of attribute changes.
|
||||
* @param attributeCollection The collection of changes to the attributes of the supported object.
|
||||
* @param ignoredUpdateHandler An optional reference to an update handler that should be ignored. This allows synchronizations of data to avoid updating the synchronizing reflection.
|
||||
*/
|
||||
private void updateReflections(AttributeSupport.AttributeCollection attributeCollection, IReflectUpdateHandler ignoredUpdateHandler) {
|
||||
if(hasReflections()) {
|
||||
IIterator iterator = getRegisteredReflections();
|
||||
|
||||
//Update the reflections.//
|
||||
while(iterator.hasNext()) {
|
||||
ReflectionHolder reflectionHolder = (ReflectionHolder) iterator.next();
|
||||
|
||||
//Don't send updates to the ignored reflection.//
|
||||
if(!Orb.checkExactEquality(reflectionHolder.getHandler(), ignoredUpdateHandler)) {
|
||||
ReflectObjectData reflectData = new ReflectObjectData();
|
||||
|
||||
//Note: this operation should not be threaded, and the attribute mask should only be used if the object is serialized (to avoid unnecessary serialization).//
|
||||
attributeCollection.attributeMask = reflectionHolder.getIsAttributeReflected();
|
||||
|
||||
if(attributeCollection.getFilteredCount() > 0) {
|
||||
if(reflectionHolder.isLocal()) {
|
||||
reflectData.data = attributeCollection.getFilteredCollection();
|
||||
}//if//
|
||||
else {
|
||||
reflectData.data = attributeCollection.getFilteredCollection();
|
||||
reflectData.data.proxyValues(IReflectableCollection.class);
|
||||
reflectData.data.proxyValues(IReflectableObject.class);
|
||||
}//else//
|
||||
|
||||
try {
|
||||
//Notify the reflection of the changes.//
|
||||
updateReflection(reflectionHolder, reflectData);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
if(Orb.isInvalidProxyException(e)) {
|
||||
//Do nothing. The reflection should be unregistered automatically.//
|
||||
}//if//
|
||||
else {
|
||||
Debug.log(e);
|
||||
}//else//
|
||||
}//catch//
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
}//if//
|
||||
}//updateReflections()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#reflectionGetLastUpdateMessageNumber(com.foundation.attribute.IReflectUpdateHandler)
|
||||
*/
|
||||
public int reflectionGetLastUpdateMessageNumber(IReflectUpdateHandler updateHandler) {
|
||||
//Note: This is here to simply make the method visible for access by the Entity's IReflectableInterface implementation.//
|
||||
return super.reflectionGetLastUpdateMessageNumber(updateHandler);
|
||||
}//reflectionGetLastUpdateMessageNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#isReflection(com.foundation.attribute.ReflectionContext)
|
||||
*/
|
||||
public boolean isReflection(ReflectionContext reflectionContext) {
|
||||
//Note: This is here to simply make the method visible for access by the Entity's IReflectableInterface implementation.//
|
||||
return super.isReflection(reflectionContext);
|
||||
}//isReflection()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#getReflectionId()
|
||||
*/
|
||||
public Object getReflectionId() {
|
||||
//Note: This is here to simply make the method visible for access by the Entity's IReflectableInterface implementation.//
|
||||
return super.getReflectionId();
|
||||
}//getReflectionId()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AbstractReflectSupport#getReflectionContext()
|
||||
*/
|
||||
public ReflectionContext getReflectionContext() {
|
||||
//Note: This is here to simply make the method visible for access by the Entity's IReflectableInterface implementation.//
|
||||
return super.getReflectionContext();
|
||||
}//getReflectionContext()//
|
||||
}//ReflectObjectSupport//
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.foundation.common.MetadataContainer;
|
||||
|
||||
public class ReflectRegistrationData implements java.io.Externalizable {
|
||||
public static byte OPTIONS_NONE = 0x00;
|
||||
public static byte OPTIONS_INCLUDE_REFLECTED = 0x01;
|
||||
public static byte OPTIONS_INCLUDE_REFLECTED_PROXY = 0x02;
|
||||
public static byte OPTIONS_DEFAULT = OPTIONS_NONE;
|
||||
|
||||
/** The optional class name which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object. */
|
||||
private String metadataIndex = null;
|
||||
/** The optional metadata container. This can be specified instead of a metadata index if custom metadata is desired. */
|
||||
private MetadataContainer metadataContainer = null;
|
||||
/** TODO: Is this still used? */
|
||||
private byte resultOptions = OPTIONS_DEFAULT;
|
||||
/**
|
||||
* ReflectRegistrationData constructor.
|
||||
*/
|
||||
public ReflectRegistrationData() {
|
||||
super();
|
||||
}//ReflectRegistrationData()//
|
||||
/**
|
||||
* ReflectRegistrationData constructor.
|
||||
* @param metadataIndex The optional class name which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object.
|
||||
*/
|
||||
public ReflectRegistrationData(String metadataIndex) {
|
||||
super();
|
||||
|
||||
this.metadataIndex = metadataIndex;
|
||||
}//ReflectRegistrationData()//
|
||||
/**
|
||||
* ReflectRegistrationData constructor.
|
||||
* @param metadataIndex The optional class which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object.
|
||||
*/
|
||||
public ReflectRegistrationData(Class metadataIndex) {
|
||||
this(metadataIndex != null ? metadataIndex.getName() : null);
|
||||
}//ReflectRegistrationData()//
|
||||
/**
|
||||
* ReflectRegistrationData constructor.
|
||||
* @param metadataContainer The optional metadata container. This can be specified instead of a metadata index if custom metadata is desired.
|
||||
*/
|
||||
public ReflectRegistrationData(MetadataContainer metadataContainer) {
|
||||
super();
|
||||
|
||||
this.metadataContainer = metadataContainer;
|
||||
}//ReflectRegistrationData()//
|
||||
/**
|
||||
* ReflectRegistrationData constructor.
|
||||
* @param metadataIndex The optional class which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object.
|
||||
* @param resultOptions Options specifying whether the result should reference the reflected object, and whether the reference should be to a proxy of the reflected object.
|
||||
* @see #OPTION_NONE
|
||||
* @see #OPTIONS_INCLUDE_REFLECTED
|
||||
* @see #OPTIONS_INCLUDE_REFLECTED_PROXY
|
||||
*/
|
||||
public ReflectRegistrationData(Class metadataIndex, byte resultOptions) {
|
||||
super();
|
||||
|
||||
this.metadataIndex = metadataIndex != null ? metadataIndex.getName() : null;
|
||||
this.resultOptions = resultOptions;
|
||||
}//ReflectRegistrationData()//
|
||||
/**
|
||||
* Gets the optional index used to lookup the metadata container.
|
||||
* @return The index used to obtain the metadata container.
|
||||
*/
|
||||
public String getMetadataIndex() {
|
||||
return metadataIndex;
|
||||
}//getMetadataIndex()//
|
||||
/**
|
||||
* Gets the optional metadata container that identifies what to reflect.
|
||||
* @return The metadata identifying what to reflect.
|
||||
*/
|
||||
public MetadataContainer getMetadataContainer() {
|
||||
return metadataContainer;
|
||||
}//getMetadataContainer()//
|
||||
/**
|
||||
* TODO: Is this still used?
|
||||
*/
|
||||
public byte getResultOptions() {
|
||||
return resultOptions;
|
||||
}//getResultOptions()//
|
||||
/**
|
||||
* 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();
|
||||
|
||||
metadataIndex = (String) in.readObject();
|
||||
metadataContainer = (MetadataContainer) in.readObject();
|
||||
resultOptions = in.readByte();
|
||||
}//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 {
|
||||
out.writeByte(0);
|
||||
out.writeObject(metadataIndex);
|
||||
out.writeObject(metadataContainer);
|
||||
out.writeByte(resultOptions);
|
||||
}//readExternal()//
|
||||
}//ReflectRegistrationData//
|
||||
2045
Foundation/src/com/foundation/attribute/ReflectionContext.java
Normal file
2045
Foundation/src/com/foundation/attribute/ReflectionContext.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.orb.Orb;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/*
|
||||
* An agent which will be sent to a remote process to create one or more reflections (actually returns reflection data objects which must be reconstituted into reflections).
|
||||
* The result of the agent is an IList of ReflectDataContainers where the last container has the last used reflection identifier that is valid.
|
||||
* The first result in each of the containers is the reflection that was asked for.
|
||||
* <p>
|
||||
* Warning: This agent is not a good way to get reflections. It is better to use metadata and load all the collection's reflections at the time the collection is reflected.
|
||||
* This is possible because all reflection contexts are single threaded meaning that they can start reflecting data and the process can use as many id's as necessary since
|
||||
* another thread cannot start loading other reflections at the same time from the same context. This agent is made to be functional, but may be inefficient.
|
||||
* </p>
|
||||
*/
|
||||
public class ReflectionCreationAgent implements IRunnable, java.io.Externalizable {
|
||||
/** The collection of references to reflectable objects requiring reflection. */
|
||||
private IList reflectables = null;
|
||||
/** The update handler that the reflected objects will notify when the reflection requires updating. */
|
||||
private IReflectUpdateHandler updateHandler = null;
|
||||
/** The optional class which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object. */
|
||||
private Class metadataIndex = null;
|
||||
|
||||
/**
|
||||
* ReflectionCreationAgent default constructor.
|
||||
*/
|
||||
public ReflectionCreationAgent() {
|
||||
}//ReflectionCreationAgent()//
|
||||
/**
|
||||
* ReflectionCreationAgent constructor.
|
||||
* @param reflectables The collection of reflectable proxies which either exist on the target process or exist furthor down the virtual network connection.
|
||||
* @param updateHandler The update handler that will handle updates from the created reflections.
|
||||
*/
|
||||
public ReflectionCreationAgent(IList reflectables, IReflectUpdateHandler updateHandler, Class metadataIndex) {
|
||||
this.reflectables = reflectables;
|
||||
this.updateHandler = Orb.isLocal(updateHandler) ? (IReflectUpdateHandler) Orb.getProxy(updateHandler, IReflectUpdateHandler.class) : updateHandler;
|
||||
}//ReflectionCreationAgent()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.thread.IRunnable#run()
|
||||
*/
|
||||
public Object run() {
|
||||
IList result = new LiteList(reflectables.getSize());
|
||||
LiteHashMap binnedReflectables = new LiteHashMap(10);
|
||||
|
||||
//Organize the reflectable references into lists indexed by the process (identified by the connectionIdentifier) they exist on, where null is the local process.//
|
||||
for(int index = 0; index < reflectables.getSize(); index++) {
|
||||
IReflectable value = (IReflectable) reflectables.get(index);
|
||||
Object connectionIdentifier = Orb.getConnectionIdentifier(value);
|
||||
IList reflectables = null;
|
||||
|
||||
//Get the collection of reflectable objects for this connection (the bin).//
|
||||
reflectables = (IList) binnedReflectables.get(connectionIdentifier);
|
||||
|
||||
//Lazily create the reflectables collection for this bin.//
|
||||
if(reflectables == null) {
|
||||
reflectables = new LiteList(this.reflectables.getSize());
|
||||
binnedReflectables.put(connectionIdentifier, reflectables);
|
||||
}//if//
|
||||
|
||||
//Add the reflectable value to the bin based on its related network connection.//
|
||||
reflectables.add(value);
|
||||
}//for//
|
||||
|
||||
//Collect the sorted reflection data into the result list.//
|
||||
if(binnedReflectables != null) {
|
||||
IIterator connectionIdentifierIterator = binnedReflectables.keyIterator();
|
||||
|
||||
while(connectionIdentifierIterator.hasNext()) {
|
||||
Object connectionIdentifier = connectionIdentifierIterator.next();
|
||||
IList reflectables = (IList) binnedReflectables.get(connectionIdentifier);
|
||||
|
||||
//If this is the local process then collect the reflectable's reflection data, otherwise forward the agent to the remote process.//
|
||||
if(connectionIdentifier == null) {
|
||||
LiteHashMap resultMap = new LiteHashMap(reflectables.getSize()); //Note: Used to handle multiple requests for the same object.//
|
||||
|
||||
for(int index = 0; index < reflectables.getSize(); index++) {
|
||||
IReflectable value = (IReflectable) reflectables.get(index);
|
||||
ReflectDataContainer reflectDataContainer = (ReflectDataContainer) resultMap.get(value);
|
||||
|
||||
//If we haven't already created the reflection data then do so now (it is possible to have more than one reference to the same object in a collection).//
|
||||
if(reflectDataContainer == null) {
|
||||
reflectDataContainer = value.zzrReflectionRegister(updateHandler, new ReflectRegistrationData(metadataIndex, ReflectRegistrationData.OPTIONS_INCLUDE_REFLECTED_PROXY));
|
||||
|
||||
if(reflectDataContainer != null) {
|
||||
resultMap.put(value, reflectDataContainer);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
result.add(reflectDataContainer);
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
ReflectionCreationAgent agent = new ReflectionCreationAgent(reflectables, updateHandler, metadataIndex);
|
||||
IList reflectionData = null;
|
||||
|
||||
//Send an agent to perform this same bit of code on the remote machine.//
|
||||
//TODO: Should handle exceptions?//
|
||||
reflectionData = (IList) Orb.sendAgent(agent, connectionIdentifier, false, 600000, null);
|
||||
|
||||
if(reflectionData != null) {
|
||||
result.addAll(reflectionData);
|
||||
}//if//
|
||||
}//else//
|
||||
}//while//
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//run()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
reflectables = (IList) in.readObject();
|
||||
updateHandler = (IReflectUpdateHandler) in.readObject();
|
||||
metadataIndex = (Class) in.readObject();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(reflectables);
|
||||
out.writeObject(updateHandler);
|
||||
out.writeObject(metadataIndex);
|
||||
}//writeExternal()//
|
||||
}//ReflectionCreationAgent//
|
||||
140
Foundation/src/com/foundation/attribute/ReflectionHolder.java
Normal file
140
Foundation/src/com/foundation/attribute/ReflectionHolder.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.orb.*;
|
||||
import com.foundation.common.MetadataContainer;
|
||||
|
||||
/**
|
||||
* Used by the reflection support classes to pair the reflection handler with an index.
|
||||
* The index allows the handler to be used with mutliple reflections when reflections are batch created.
|
||||
* Each index represents one reflection that the handler manages messages for.
|
||||
*/
|
||||
public class ReflectionHolder {
|
||||
/** The remote identifier for the reflection. */
|
||||
private long reflectionId = 0;
|
||||
/** The handler called when the reflection requires updating due to changes in the reflected value. */
|
||||
private IReflectUpdateHandler handler = null;
|
||||
/** The listener for a lost connection to remote reflections. */
|
||||
private IInvalidProxyListener listener = null;
|
||||
/** The array of flags indicating which attributes were reflected (and thus which attribute value changes should be sent to the reflection). Collection reflections ignore this attribute. */
|
||||
private boolean[] isAttributeReflected = null;
|
||||
/** The optional metadata used for determining what is reflected. */
|
||||
private MetadataContainer metadataContainer = null;
|
||||
/**
|
||||
* ReflectionHolder constructor.
|
||||
* Constructs a reflection holder for an object reflection.
|
||||
*/
|
||||
public ReflectionHolder(IReflectUpdateHandler handler, long reflectionId, boolean[] isAttributeReflected, MetadataContainer metadataContainer) {
|
||||
super();
|
||||
|
||||
this.handler = handler;
|
||||
this.reflectionId = reflectionId;
|
||||
this.isAttributeReflected = isAttributeReflected;
|
||||
this.metadataContainer = metadataContainer;
|
||||
}//ReflectionHolder()//
|
||||
/**
|
||||
* ReflectionHolder constructor.
|
||||
* Constructs a reflection holder for a collection reflection.
|
||||
*/
|
||||
public ReflectionHolder(IReflectUpdateHandler handler, long reflectionId, MetadataContainer metadataContainer) {
|
||||
super();
|
||||
|
||||
this.handler = handler;
|
||||
this.reflectionId = reflectionId;
|
||||
this.metadataContainer = metadataContainer;
|
||||
}//ReflectionHolder()//
|
||||
/**
|
||||
* @return Returns the reflectionId.
|
||||
*/
|
||||
public long getReflectionId() {
|
||||
return reflectionId;
|
||||
}//getReflectionId()//
|
||||
/**
|
||||
* Gets the handler called when the reflection requires updating due to changes in the reflected value.
|
||||
* @return The update handler for the reflection.
|
||||
*/
|
||||
public IReflectUpdateHandler getHandler() {
|
||||
return handler;
|
||||
}//getHandler()//
|
||||
/**
|
||||
* Gets the listener for a lost connection to remote reflections.
|
||||
* @return The invalid proxy listener registered with the reflection's update handler proxy (if the reflection is remote).
|
||||
*/
|
||||
public IInvalidProxyListener getListener() {
|
||||
return listener;
|
||||
}//getListener()//
|
||||
/**
|
||||
* Gets the array of flags indicating which attributes were reflected (and thus which attribute value changes should be sent to the reflection).
|
||||
* Collection reflections ignore this attribute.
|
||||
* @return The metadata regarding the reflection's already reflected attributes.
|
||||
*/
|
||||
public boolean[] getIsAttributeReflected() {
|
||||
return isAttributeReflected;
|
||||
}//getIsAttributeReflected()//
|
||||
/**
|
||||
* Sets the array of flags indicating which attributes were reflected (and thus which attribute value changes should be sent to the reflection).
|
||||
* Collection reflections ignore this attribute.
|
||||
* @param isAttributeReflected The metadata regarding the reflection's already reflected attributes.
|
||||
*/
|
||||
public void setIsAttributeReflected(boolean[] isAttributeReflected) {
|
||||
this.isAttributeReflected = isAttributeReflected;
|
||||
}//setIsAttributeReflected()//
|
||||
/**
|
||||
* Gets the optional metadata used for determining what is reflected.
|
||||
* @return The reflection metadata or null if no metadata is available.
|
||||
*/
|
||||
public MetadataContainer getMetadataContainer() {
|
||||
return metadataContainer;
|
||||
}//getMetadataContainer()//
|
||||
/**
|
||||
* Checks to see if the value is equivalent to this value.
|
||||
* @param value Either a reflection holder or an update handler.
|
||||
* @return Whether the two objects are equivalent.
|
||||
*/
|
||||
public boolean equals(Object value) {
|
||||
IReflectUpdateHandler handler = Orb.isProxy(this.handler) && Orb.isLocal(this.handler) ? (IReflectUpdateHandler) Orb.getLocal(this.handler) : this.handler;
|
||||
|
||||
return (value instanceof ReflectionHolder ? ((ReflectionHolder) value).handler : (IReflectUpdateHandler) value).equals(handler);
|
||||
}//equals()//
|
||||
/**
|
||||
* Gets a hash value representing the holder.
|
||||
* @return A representative semi-unique number.
|
||||
*/
|
||||
public int hashCode() {
|
||||
return handler.hashCode();
|
||||
}//hashCode()//
|
||||
/**
|
||||
* Determines whether the reflection is in this process.
|
||||
* @return Whether the reflection is local to this process.
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return Orb.isLocal(handler);
|
||||
}//isLocal()//
|
||||
/**
|
||||
* Registers a disconnect handler if necessary.
|
||||
* @param reflected The object being reflected.
|
||||
*/
|
||||
public void registerDisconnectHandler(final IReflectable reflected) {
|
||||
if((Orb.isProxy(handler)) && (!Orb.isLocal(handler))) {
|
||||
Orb.registerInvalidProxyListener(handler, listener = new IInvalidProxyListener() {
|
||||
public void evaluate(Object proxy) {
|
||||
reflected.reflectionUnregister((IReflectUpdateHandler) proxy);
|
||||
}//evaluate()//
|
||||
});
|
||||
}//if//
|
||||
}//registerDisconnectHandler()//
|
||||
/**
|
||||
* Unregisters the disconnect handler if one was necessary.
|
||||
*/
|
||||
public void unregisterDisconnectHandler() {
|
||||
if(listener != null) {
|
||||
Orb.unregisterInvalidProxyListener(handler, listener);
|
||||
}//if//
|
||||
}//unregisterDisconnectHandler()//
|
||||
}//ReflectionHolder//
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.attribute;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.common.thread.ISingleThreadedContext;
|
||||
import com.common.thread.ThreadService;
|
||||
import com.common.util.Queue;
|
||||
import com.foundation.event.IRequestHandler;
|
||||
|
||||
/**
|
||||
* Uses a dedicated thread to manage the reflection context.
|
||||
* The user calls the execute methods to have functionality get executed by the dedicated thread which has authority to interact with the reflections under this context.
|
||||
*/
|
||||
public class SingleThreadedReflectionContext extends ReflectionContext implements ISingleThreadedContext {
|
||||
private static final int WAIT_TIME = 1000;
|
||||
|
||||
private volatile boolean isStopped = true;
|
||||
private Queue requestQueue = new Queue(10);
|
||||
private volatile Thread requestThread = null;
|
||||
|
||||
/**
|
||||
* A simple runnable that wrappers another runnable and notifies waiting threads when the execution is complete.
|
||||
*/
|
||||
private static class SynchronousRunnable implements Runnable {
|
||||
private IRunnable runnable = null;
|
||||
private Object result = null;
|
||||
private boolean hasResult = false;
|
||||
|
||||
/**
|
||||
* SynchronousRunnable constructor.
|
||||
* @param runnable The runnable that will be executed and whose result will be passed to waiting threads.
|
||||
*/
|
||||
public SynchronousRunnable(IRunnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}//SynchronousRunnable()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run()
|
||||
*/
|
||||
public void run() {
|
||||
Object result = runnable.run();
|
||||
|
||||
synchronized(this) {
|
||||
hasResult = true;
|
||||
this.result = result;
|
||||
notifyAll();
|
||||
}//synchronized//
|
||||
}//run()//
|
||||
/**
|
||||
* Gets the result of the runnable after it has completed.
|
||||
* <p>Warning: This is a blocking operation.</p>
|
||||
* @return The wrappered runnable's result.
|
||||
*/
|
||||
public synchronized Object getResult() {
|
||||
if(!hasResult) {
|
||||
try {
|
||||
wait(0);
|
||||
}//try//
|
||||
catch(InterruptedException e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getResult()//
|
||||
}//SynchronousRunnable//
|
||||
|
||||
/**
|
||||
* A request handler which single threads all incomming requests via the single threaded reflection context.
|
||||
*/
|
||||
private static class RequestHandler implements IRequestHandler {
|
||||
private SingleThreadedReflectionContext context = null;
|
||||
|
||||
public RequestHandler() {
|
||||
}//RequestHandler()//
|
||||
public boolean isRunning() {
|
||||
return !context.isStopped();
|
||||
}//isRunning()//
|
||||
public Object execute(final IRunnable runnable, boolean synchronous) {
|
||||
Object result = null;
|
||||
|
||||
if(synchronous) {
|
||||
SynchronousRunnable queuedRunnable = new SynchronousRunnable(runnable);
|
||||
|
||||
synchronized(context) {
|
||||
context.requestQueue.enqueue(queuedRunnable);
|
||||
}//synchronized//
|
||||
|
||||
result = queuedRunnable.getResult();
|
||||
}//if//
|
||||
else {
|
||||
synchronized(context) {
|
||||
context.requestQueue.enqueue(new Runnable() {
|
||||
public void run() {
|
||||
runnable.run();
|
||||
}//run()//
|
||||
});
|
||||
}//synchronized//
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//execute()//
|
||||
public void executeAsync(IRunnable runnable) {
|
||||
execute(runnable, false);
|
||||
}//executeAsync()//
|
||||
public Object execute(IRunnable runnable) {
|
||||
return execute(runnable, true);
|
||||
}//execute()//
|
||||
public boolean isRequestThread() {
|
||||
return Thread.currentThread() == context.requestThread;
|
||||
}//isRequestThread()//
|
||||
public void setContext(SingleThreadedReflectionContext context) {
|
||||
this.context = context;
|
||||
}//setContext()//
|
||||
}//RequestHandler//
|
||||
/**
|
||||
* SingleThreadedReflectionContext constructor.
|
||||
*/
|
||||
public SingleThreadedReflectionContext() {
|
||||
this(null);
|
||||
}//SingleThreadedReflectionContext()//
|
||||
/**
|
||||
* SingleThreadedReflectionContext constructor.
|
||||
* @param metadataIndex The optional class which defines metadata used for determining what is reflected. The defining class' metadata will be indexed in the metadata service by the class object.
|
||||
*/
|
||||
public SingleThreadedReflectionContext(Class metadataIndex) {
|
||||
super(new RequestHandler(), metadataIndex, null);
|
||||
|
||||
((RequestHandler) getRequestHandler()).setContext(this);
|
||||
}//SingleThreadedReflectionContext()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.ReflectionContext#internalInitialize()
|
||||
*/
|
||||
protected void internalInitialize() {
|
||||
super.internalInitialize();
|
||||
|
||||
isStopped = false;
|
||||
ThreadService.run(new Runnable() {
|
||||
public void run() {
|
||||
ThreadService.setIsInSingleThreadedContext(SingleThreadedReflectionContext.this);
|
||||
requestThread = Thread.currentThread();
|
||||
processRequests();
|
||||
}//run()//
|
||||
});
|
||||
}//internalInitialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.ReflectionContext#internalRelease()
|
||||
*/
|
||||
protected void internalRelease() {
|
||||
super.internalRelease();
|
||||
isStopped = true;
|
||||
}//internalRelease()//
|
||||
/**
|
||||
* Begins a request processing loop that will run until the context is released.
|
||||
*/
|
||||
private void processRequests() {
|
||||
boolean stop = false;
|
||||
|
||||
while(!stop) {
|
||||
try {
|
||||
Runnable request = null;
|
||||
|
||||
synchronized(this) {
|
||||
stop = this.isStopped;
|
||||
|
||||
if(requestQueue.getSize() == 0) {
|
||||
wait(WAIT_TIME);
|
||||
}//if//
|
||||
|
||||
if(requestQueue.getSize() > 0) {
|
||||
request = (Runnable) requestQueue.dequeue();
|
||||
}//if//
|
||||
}//synchronized//
|
||||
|
||||
if(request != null) {
|
||||
request.run();
|
||||
}//if//
|
||||
}//try//
|
||||
catch(InterruptedException e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//while//
|
||||
}//processRequests()//
|
||||
/**
|
||||
* Determines whether the context is currently not running.
|
||||
* @return Whether the reflection context is non-operational.
|
||||
*/
|
||||
private boolean isStopped() {
|
||||
return isStopped;
|
||||
}//isStopped()//
|
||||
}//SingleThreadedReflectionContext//
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 1999,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.attribute;
|
||||
|
||||
/**
|
||||
* Reports the cause of a failure in the synchronization of a reflection.
|
||||
*/
|
||||
public class SynchronizeFailureCause {
|
||||
/** This cause indicates that the failure occured because the local model was not up to date. The synchronization will not return until the model is up to date, so the caller can retry if so desired. */
|
||||
public static final int ID_MISSING_UPDATES = 1;
|
||||
/** The unkown cause. This cause indicates that the system couldn't pinpoint a reason for the failure. */
|
||||
public static final int ID_UNKOWN = 0;
|
||||
|
||||
private int failureId = ID_UNKOWN;
|
||||
private Object cause = null;
|
||||
/**
|
||||
* SynchronizeFailureCause constructor.
|
||||
* @param failureId The cause of the failure. This should only be null for predefined failure causes (see ReflectionContext's FAILURE_CAUSE_xx identifiers).
|
||||
*/
|
||||
public SynchronizeFailureCause(int failureId) {
|
||||
this.failureId = failureId;
|
||||
}//SynchronizeFailureCause()//
|
||||
/**
|
||||
* SynchronizeFailureCause constructor.
|
||||
* @param failureId The cause identifier. One of the FailureCause.ID_xxx identifiers.
|
||||
* @param cause The cause of the failure. This should only be null for predefined failure causes (see ReflectionContext's FAILURE_CAUSE_xx identifiers).
|
||||
*/
|
||||
public SynchronizeFailureCause(int failureId, Object cause) {
|
||||
this.failureId = failureId;
|
||||
this.cause = cause;
|
||||
}//SynchronizeFailureCause()//
|
||||
/**
|
||||
* SynchronizeFailureCause constructor.
|
||||
* @param cause The cause of the failure. This should only be null for predefined failure causes (see ReflectionContext's FAILURE_CAUSE_xx identifiers).
|
||||
*/
|
||||
public SynchronizeFailureCause(Object cause) {
|
||||
this.cause = cause;
|
||||
}//SynchronizeFailureCause()//
|
||||
/**
|
||||
* Gets the identifier of the failure.
|
||||
* @return One of the FailureCause.ID_xxx identifiers.
|
||||
*/
|
||||
public int getFailureId() {
|
||||
return failureId;
|
||||
}//getFailureId()//
|
||||
/**
|
||||
* A custom cause value.
|
||||
* @return The failure cause value. This will be null for predefined causes (see ReflectionContext's FAILURE_CAUSE_xx identifiers).
|
||||
*/
|
||||
public Object getCause() {
|
||||
return cause;
|
||||
}//getCause()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return failureId == ID_UNKOWN ? "Reflection Sync Failure: " + cause : failureId == ID_MISSING_UPDATES ? "Reflection Sync Failure: Missing Updates." : "Reflection Sync Failure: Unexpected.";
|
||||
}//toString()//
|
||||
}//SynchronizeFailureCause//
|
||||
22
Foundation/src/com/foundation/clone/ICloneable.java
Normal file
22
Foundation/src/com/foundation/clone/ICloneable.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2003,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.clone;
|
||||
|
||||
import com.foundation.common.MetadataContainer;
|
||||
import com.foundation.metadata.CloneContext;
|
||||
|
||||
public interface ICloneable extends Cloneable {
|
||||
public static final MetadataContainer EMPTY_METADATA = new MetadataContainer();
|
||||
/**
|
||||
* Creates a clone of the implementing object. A clone is usually a shallow copy of the object, but a clone may be a shallow copy of a composite object as well.
|
||||
* @param context The optional cloning context which tracks clone creation to prevent duplicate clones to the same object. A context will be created if not supplied.
|
||||
* @param metadata The metadata used for the cloning. If none is provided, or if an object is encountered for which there is no metadata, then a best guess will be taken (ie all immutable data and part-of data that is available will be cloned).
|
||||
* @return This instance's clone.
|
||||
*/
|
||||
public Object cloneObject(CloneContext context, MetadataContainer metadata);
|
||||
}//ICloneable//
|
||||
309
Foundation/src/com/foundation/common/AttributeBinding.java
Normal file
309
Foundation/src/com/foundation/common/AttributeBinding.java
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2008 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.common;
|
||||
|
||||
import com.foundation.event.*;
|
||||
import com.foundation.event.model.Binding;
|
||||
import com.foundation.event.model.IModelListenerAttributeHandler;
|
||||
import com.foundation.event.model.IModelListenerCollectionHandler;
|
||||
import com.foundation.event.model.ModelListener;
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
/*
|
||||
* The binding's results (the attribute values) are provided to the listener as input to other bindings.
|
||||
* <p>
|
||||
* Note: Attributes whose values have not yet been assigned will not be loaded (if lazy loadable). The value IModelListenerAttributeHandler.NOT_SET will be used instead.
|
||||
* </p>
|
||||
*/
|
||||
public class AttributeBinding extends Binding {
|
||||
/** This value is used in place of an attribute value if the attribute has not yet been initialized. @see IModelListenerAttributeHandler.NOT_SET */
|
||||
protected static final Object NOT_SET = IModelListenerAttributeHandler.NOT_SET;
|
||||
|
||||
/** This flag may be passed in the constructor to indicate that the binding should force the attribute to be lazy loaded if there isn't yet a value and it is lazy loadable. */
|
||||
public static final int FLAG_LAZY_LOAD_ATTRIBUTE = 0x02;
|
||||
|
||||
/** The attribute that this binding is using for events and values. */
|
||||
private int attribute;
|
||||
/** The optional handler which is notified when the attribute value changes, a new attribute value is discovered, or a previous attribute value is removed. The attribute value is discovered or removed if the object that has the attribute is added or removed to/from the model listener. */
|
||||
private IModelListenerAttributeHandler attributeHandler = null;
|
||||
/** The optional handlers which are notified when the attribute's value is a collection and the collection adds or removes a value. The handler is called for every value in the collection when the collection is first discovered or removed. Multiple handlers are allowed since a collection can contain other collections (in which case the first level collection calls the first level handler, etc). */
|
||||
private IModelListenerCollectionHandler[] collectionHandlers = null;
|
||||
|
||||
protected static class AttributeEntityMetadata extends Binding.Metadata implements IEventHandler {
|
||||
/** The last known value for the entity's attribute. */
|
||||
private Object value = null;
|
||||
|
||||
/**
|
||||
* AttributeEntityMetadata constructor.
|
||||
* @param binding The binding that created this metadata.
|
||||
* @param source The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener.
|
||||
* @param entity The entity the metadata describes.
|
||||
*/
|
||||
public AttributeEntityMetadata(Binding binding, Binding.Metadata source, IEntity entity) {
|
||||
super(binding, source, entity);
|
||||
}//AttributeEntityMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Binding.Metadata#getCollectionModelListener(int)
|
||||
*/
|
||||
protected IModelListenerCollectionHandler getCollectionModelListener(int collectionDepth) {
|
||||
return null; //TODO:
|
||||
}//getCollectionModelListener()//
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}//getValue()//
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}//setValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Never called.//
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
if(EventSupport.isStandardEvent(flags) || EventSupport.isTrustedChangeEvent(flags)) {
|
||||
AttributeBinding binding = (AttributeBinding) getBinding();
|
||||
IEntity entity = (IEntity) getObject();
|
||||
IModelListenerAttributeHandler handler = binding.getAttributeHandler();
|
||||
Object newValue = entity.getAttributeSupport().getAttributeValue(binding.getAttribute());
|
||||
Object oldValue = getValue();
|
||||
|
||||
binding.changeStarted();
|
||||
|
||||
//TODO: The entity (eventEmitter) has fired an event we were listening for. The attribute's value has changed.
|
||||
if(handler != null) {
|
||||
if((oldValue == NOT_SET) && (EventSupport.isTrustedChangeEvent(flags))) {
|
||||
handler.run(getObject(handler.getReferenceType(), true), IModelListenerAttributeHandler.TYPE_INITIALIZED, 0, null, newValue);
|
||||
}//if//
|
||||
else {
|
||||
handler.run(getObject(handler.getReferenceType(), true), IModelListenerAttributeHandler.TYPE_CHANGED, 0, oldValue, newValue);
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
//Notify the model listener of the value that is no longer in its scope.//
|
||||
if((oldValue != null) && (oldValue != NOT_SET)) {
|
||||
((AttributeBinding) getBinding()).valueRemoved(this, oldValue, 0);
|
||||
}//if//
|
||||
|
||||
//Save the attribute's value so we know what the old value was when we are notified of a change.//
|
||||
setValue(newValue);
|
||||
|
||||
//Notify the model listener of the value that is in its scope.//
|
||||
if((newValue != null) && (newValue != NOT_SET)) {
|
||||
((AttributeBinding) getBinding()).valueAdded(this, newValue, EventSupport.isTrustedChangeEvent(flags) ? ModelListener.INTERNAL_FLAG_INITIALIZING : 0);
|
||||
}//if//
|
||||
|
||||
binding.changeStopped();
|
||||
}//if//
|
||||
}//evaluate()//
|
||||
}//AttributeEntityMetadata//
|
||||
/**
|
||||
* Determines whether the attribute has not yet been initialized based on the flags.
|
||||
* @param flags The flags from the IModelListenerAttributeHandler.
|
||||
* @return Whether the attribute is not yet initialized.
|
||||
*/
|
||||
public static boolean isAttributeUninitialized(int flags) {
|
||||
return (flags & IModelListenerAttributeHandler.FLAG_ATTRIBUTE_UNINITIALIZED) > 0;
|
||||
}//isAttributeUninitialized()//
|
||||
/**
|
||||
* Determines whether the event is occuring while the model listener is in the process of initializing.
|
||||
* @param flags The flags from the IModelListenerAttributeHandler.
|
||||
* @return Whether the listener is initializing.
|
||||
*/
|
||||
public static boolean isListenerInitializing(int flags) {
|
||||
return (flags & IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING) > 0;
|
||||
}//isListenerInitializing()//
|
||||
/**
|
||||
* Determines whether the event is occuring while the model listener is in the process of releasing.
|
||||
* @param flags The flags from the IModelListenerAttributeHandler.
|
||||
* @return Whether the listener is releasing.
|
||||
*/
|
||||
public static boolean isListenerReleasing(int flags) {
|
||||
return (flags & IModelListenerAttributeHandler.FLAG_LISTENER_RELEASING) > 0;
|
||||
}//isListenerReleasing()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param attribute The attribute used by the binding.
|
||||
*/
|
||||
public AttributeBinding(Class entityType, int attribute) {
|
||||
super(entityType, 0);
|
||||
this.attribute = attribute;
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param flags The applicable flags for this binding, or zero.
|
||||
* @param attribute The attribute used by the binding.
|
||||
*/
|
||||
public AttributeBinding(Class entityType, int attribute, int flags) {
|
||||
super(entityType, flags);
|
||||
this.attribute = attribute;
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param flags The applicable flags for this binding, or zero.
|
||||
* @param attribute The attribute used by the binding.
|
||||
* @param attributeHandler The optional handler which is notified when the attribute value changes, a new attribute value is discovered, or a previous attribute value is removed. The attribute value is discovered or removed if the object that has the attribute is added or removed to/from the model listener.
|
||||
* @param collectionHandler The optional handlers which are notified when the attribute's value is a collection and the collection adds or removes a value. The handler is called for every value in the collection when the collection is first discovered or removed. Multiple handlers are allowed since a collection can contain other collections (in which case the first level collection calls the first level handler, etc).
|
||||
*/
|
||||
public AttributeBinding(Class entityType, int attribute, int flags, IModelListenerAttributeHandler attributeHandler, IModelListenerCollectionHandler[] collectionHandlers) {
|
||||
super(entityType, flags);
|
||||
this.attribute = attribute;
|
||||
this.attributeHandler = attributeHandler;
|
||||
this.collectionHandlers = collectionHandlers;
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param attribute The attribute used by the binding.
|
||||
*/
|
||||
public AttributeBinding(Class entityType, Attribute attribute) {
|
||||
super(entityType, 0);
|
||||
this.attribute = attribute.getNumber();
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param flags The applicable flags for this binding, or zero.
|
||||
* @param attribute The attribute used by the binding.
|
||||
*/
|
||||
public AttributeBinding(Class entityType, Attribute attribute, int flags) {
|
||||
super(entityType, flags);
|
||||
this.attribute = attribute.getNumber();
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* AttributeBinding constructor.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param flags The applicable flags for this binding, or zero.
|
||||
* @param attribute The attribute used by the binding.
|
||||
* @param attributeHandler The optional handler which is notified when the attribute value changes, a new attribute value is discovered, or a previous attribute value is removed. The attribute value is discovered or removed if the object that has the attribute is added or removed to/from the model listener.
|
||||
* @param collectionHandler The optional handlers which are notified when the attribute's value is a collection and the collection adds or removes a value. The handler is called for every value in the collection when the collection is first discovered or removed. Multiple handlers are allowed since a collection can contain other collections (in which case the first level collection calls the first level handler, etc).
|
||||
*/
|
||||
public AttributeBinding(Class entityType, Attribute attribute, int flags, IModelListenerAttributeHandler attributeHandler, IModelListenerCollectionHandler[] collectionHandlers) {
|
||||
super(entityType, flags);
|
||||
this.attribute = attribute.getNumber();
|
||||
this.attributeHandler = attributeHandler;
|
||||
this.collectionHandlers = collectionHandlers;
|
||||
}//AttributeBinding()//
|
||||
/**
|
||||
* Determines whether this binding should force the lazy load to be called if the attribute is not yet initialized.
|
||||
* @return Whether the lazy load should be called for the attribute if a value is not yet set.
|
||||
*/
|
||||
public boolean lazyLoadAttribute() {
|
||||
return (getFlags() & FLAG_LAZY_LOAD_ATTRIBUTE) > 0;
|
||||
}//lazyLoadAttribute()//
|
||||
/**
|
||||
* Gets the attribute number that this binding uses.
|
||||
* @return The attribute bound by this binding.
|
||||
*/
|
||||
protected int getAttribute() {
|
||||
return attribute;
|
||||
}//getAttribute()//
|
||||
/**
|
||||
* Gets the optional handler that is to be called when the attribute values are changed.
|
||||
* @return The handler called when an attribute's value changes, or when an object reference containing the attributes is added or removed.
|
||||
*/
|
||||
protected IModelListenerAttributeHandler getAttributeHandler() {
|
||||
return attributeHandler;
|
||||
}//getAttributeHandler()//
|
||||
/**
|
||||
* Gets the optional handlers called when the attribute value is a collection whose values are changed.
|
||||
* @return Array of collection changed handlers used to notify if the attribute's value is a collection (or collection of collections, etc). The depth of the collection that changed is the index into this array.
|
||||
*/
|
||||
protected IModelListenerCollectionHandler[] getCollectionHandlers() {
|
||||
return collectionHandlers;
|
||||
}//getCollectionHandlers()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Binding#internalCreateMetadata(com.foundation.common.Binding.Metadata, java.lang.Object)
|
||||
*/
|
||||
protected Binding.Metadata internalCreateMetadata(Binding.Metadata source, Object object) {
|
||||
return new AttributeEntityMetadata(this, source, (IEntity) object);
|
||||
}//internalCreateMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#internalInitialize(com.foundation.event.model.Binding.Metadata, int)
|
||||
*/
|
||||
protected void internalInitialize(Binding.Metadata metadata, int flags) {
|
||||
AttributeEntityMetadata attributeMetadata = (AttributeEntityMetadata) metadata;
|
||||
IEntity entity = (IEntity) metadata.getObject();
|
||||
|
||||
//Save the attribute's value so we know what the old value was when we are notified of a change.//
|
||||
attributeMetadata.setValue(lazyLoadAttribute() || entity.getAttributeSupport().getIsAttributeRealized(attribute) ? entity.getAttributeSupport().getAttributeValue(attribute) : NOT_SET);
|
||||
//Register the listener to the entity for its attribute's value.//
|
||||
getEventSupport().register(entity, attribute, attributeMetadata, true);
|
||||
|
||||
//Notify the event handler that the value has been added.//
|
||||
if(getAttributeHandler() != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
|
||||
|
||||
if(attributeMetadata.getValue() == NOT_SET) {
|
||||
handlerFlags |= IModelListenerAttributeHandler.FLAG_ATTRIBUTE_UNINITIALIZED;
|
||||
}//if//
|
||||
|
||||
getAttributeHandler().run(attributeMetadata.getObject(getAttributeHandler().getReferenceType(), true), IModelListenerAttributeHandler.TYPE_ADDED, handlerFlags, null, attributeMetadata.getValue() == NOT_SET ? null : attributeMetadata.getValue());
|
||||
}//if//
|
||||
|
||||
//Notify the model listener of the value that is now in its scope.//
|
||||
if((attributeMetadata.getValue() != null) && (attributeMetadata.getValue() != NOT_SET)) {
|
||||
valueAdded(metadata, attributeMetadata.getValue(), flags);
|
||||
}//if//
|
||||
}//internalInitialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Binding#internalRelease(com.foundation.common.Binding.Metadata, int)
|
||||
*/
|
||||
protected void internalRelease(Binding.Metadata metadata, int flags) {
|
||||
AttributeEntityMetadata attributeMetadata = (AttributeEntityMetadata) metadata;
|
||||
|
||||
//Unregister the listener to the entity for its attribute's value.//
|
||||
getEventSupport().unregister((IEntity) metadata.getObject(), attribute, attributeMetadata);
|
||||
|
||||
//Notify the model listener of the value that is no longer in its scope.//
|
||||
if((attributeMetadata.getValue() != null) && (attributeMetadata.getValue() != NOT_SET)) {
|
||||
valueRemoved(metadata, attributeMetadata.getValue(), flags);
|
||||
}//if//
|
||||
|
||||
//Notify the event handler that the value has been removed.//
|
||||
if(getAttributeHandler() != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_RELEASING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_RELEASING : 0;
|
||||
|
||||
if(attributeMetadata.getValue() == NOT_SET) {
|
||||
handlerFlags |= IModelListenerAttributeHandler.FLAG_ATTRIBUTE_UNINITIALIZED;
|
||||
}//if//
|
||||
|
||||
getAttributeHandler().run(attributeMetadata.getObject(getAttributeHandler().getReferenceType(), true), IModelListenerAttributeHandler.TYPE_REMOVED, handlerFlags, attributeMetadata.getValue() == NOT_SET ? null : attributeMetadata.getValue(), null);
|
||||
}//if//
|
||||
}//internalRelease()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#getMetadata(java.lang.Object, com.foundation.event.model.Binding)
|
||||
*/
|
||||
protected Metadata getMetadata(Object value, Metadata source) {
|
||||
return getMetadata(new AttributeEntityMetadata(this, source, (IEntity) value));
|
||||
}//getMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#debug(java.lang.StringBuffer, com.foundation.event.model.Binding.Metadata, int, com.foundation.event.model.ModelListener.DebugHandler)
|
||||
*/
|
||||
protected void debug(StringBuffer buffer, Binding.Metadata metadata, int tabDepth, ModelListener.DebugHandler handler) {
|
||||
AttributeEntityMetadata attributeMetadata = (AttributeEntityMetadata) metadata;
|
||||
IEntity entity = (IEntity) metadata.getObject();
|
||||
|
||||
for(int index = 0; index < tabDepth; index++) {
|
||||
buffer.append('\t');
|
||||
}//for//
|
||||
|
||||
buffer.append("(.");
|
||||
buffer.append(entity.getAttributeName(attribute));
|
||||
buffer.append(")\r\n");
|
||||
|
||||
//Navigate to the value's bindings.//
|
||||
debug(buffer, attributeMetadata, attributeMetadata.getValue(), tabDepth + 1, handler);
|
||||
}//debug()//
|
||||
}//AttributeBinding//
|
||||
42
Foundation/src/com/foundation/common/BackReferenceNode.java
Normal file
42
Foundation/src/com/foundation/common/BackReferenceNode.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 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.common;
|
||||
|
||||
/**
|
||||
* Used to maintain a linked list of back references within entities and other containment supporting classes (such as ManagedCollection).
|
||||
* Maintaining weak link back references may require multiple back references if an entity is in multiple collections marked part-of and weak in the same logical object, but the count would rarely go above one and very rarely above two, so a linked list is fairly efficient IMO.
|
||||
*/
|
||||
public class BackReferenceNode {
|
||||
private Object reference;
|
||||
private BackReferenceNode next = null;
|
||||
/**
|
||||
* BackReferenceNode constructor.
|
||||
* @param reference The object being referenced by the node.
|
||||
*/
|
||||
public BackReferenceNode(Object reference) {
|
||||
this.reference = reference;
|
||||
}//BackReferenceNode()//
|
||||
/**
|
||||
* BackReferenceNode constructor.
|
||||
* @param reference The object being referenced by the node.
|
||||
* @param next The next node in the chain.
|
||||
*/
|
||||
public BackReferenceNode(Object reference, BackReferenceNode next) {
|
||||
this.reference = reference;
|
||||
this.next = next;
|
||||
}//BackReferenceNode()//
|
||||
public Object getReference() {
|
||||
return reference;
|
||||
}//getReference()//
|
||||
public BackReferenceNode getNext() {
|
||||
return next;
|
||||
}//getNext()//
|
||||
public void setNext(BackReferenceNode next) {
|
||||
this.next = next;
|
||||
}//setNext()//
|
||||
}//BackReferenceNode//
|
||||
93
Foundation/src/com/foundation/common/CompositeEntityKey.java
Normal file
93
Foundation/src/com/foundation/common/CompositeEntityKey.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package com.foundation.common;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* Combines multiple attribute values together into a single key value that can be compared and serialized.
|
||||
*/
|
||||
public class CompositeEntityKey implements IExternalizable, Externalizable {
|
||||
private Object[] values;
|
||||
/**
|
||||
* CompositeEntityKey constructor.
|
||||
* @param values The non-null array of values that make up the key. The array must have a length > 1.
|
||||
*/
|
||||
public CompositeEntityKey(Object[] values) {
|
||||
if(values == null || values.length < 2) {
|
||||
throw new IllegalArgumentException();
|
||||
}//if//
|
||||
|
||||
this.values = values;
|
||||
}//CompositeEntityKey()//
|
||||
/**
|
||||
* Gets the part of the key with the given index.
|
||||
* @param index The zero based index of the key part.
|
||||
* @return The key part.
|
||||
*/
|
||||
public Object getPart(int index) {
|
||||
return values[index];
|
||||
}//getPart()//
|
||||
/**
|
||||
* Gets the count of parts in the key.
|
||||
* @return The part count.
|
||||
*/
|
||||
public int getPartCount() {
|
||||
return values.length;
|
||||
}//getPartCount()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
|
||||
for(int index = 0; index < values.length; index++) {
|
||||
result ^= values[index].hashCode();
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//hashCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
boolean result = object instanceof CompositeEntityKey && ((CompositeEntityKey) object).values.length == values.length;
|
||||
|
||||
for(int index = 0; result && index < values.length; index++) {
|
||||
result = Comparator.equals(values[index], ((CompositeEntityKey) object).values[index]);
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeObject(values);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
values = (Object[]) in.readObject();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeObject(values);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
values = (Object[]) in.readObject();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
}//CompositeEntityKey//
|
||||
3323
Foundation/src/com/foundation/common/Entity.java
Normal file
3323
Foundation/src/com/foundation/common/Entity.java
Normal file
File diff suppressed because it is too large
Load Diff
183
Foundation/src/com/foundation/common/EntityMetadata.java
Normal file
183
Foundation/src/com/foundation/common/EntityMetadata.java
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2008 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.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.optimized.IIntIterator;
|
||||
import com.common.util.optimized.IntHashSet;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
import com.foundation.metadata.TypeMetadata;
|
||||
|
||||
/*
|
||||
* This type metadata defines a collection of attributes for a class.
|
||||
* This metadata may be used in many places, the most common are for defining which attributes of a class to reflect by default, and which attributes to clone when calling IEntity.cloneObject().
|
||||
* <p><b>This is an immutable object.</b></p>
|
||||
* <p>TODO: Protect the attribute's int array somehow. We could switch to an IntArray and add an immutable flag to the object.</p>
|
||||
*/
|
||||
public class EntityMetadata implements IMetadata {
|
||||
/** The class of IEntity the metadata applies to. */
|
||||
private Class type = null;
|
||||
/** The attributes of the IEntity that are to be included. */
|
||||
private IntHashSet attributes = null;
|
||||
/**
|
||||
* EntityMetadata constructor.
|
||||
*/
|
||||
public EntityMetadata() {
|
||||
super();
|
||||
}//EntityMetadata()//
|
||||
/**
|
||||
* EntityMetadata constructor.
|
||||
* @param type The entity class that the metadata applies to.
|
||||
* @param attributes The attributes that will be included or excluded.
|
||||
* @param inclusive Whether the given attributes are to be included. If false, the metadata user will exclude the attributes passed and include all other attributtes for the type passed, (any attributes not explicitly excluded).
|
||||
*/
|
||||
public EntityMetadata(Class type, int[] attributes, boolean inclusive) {
|
||||
super();
|
||||
|
||||
if(!IEntity.class.isAssignableFrom(type) || attributes == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}//if//
|
||||
|
||||
this.type = type;
|
||||
|
||||
//Convert the exclusive attributes array into an inclusive one.//
|
||||
if(!inclusive) {
|
||||
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
int totalCount = metadata.getLastAttributeNumber();
|
||||
IntHashSet attributesSet = new IntHashSet(attributes, false);
|
||||
|
||||
this.attributes = new IntHashSet(totalCount - attributes.length);
|
||||
|
||||
for(int index = 0; index <= totalCount; index++) {
|
||||
if(!attributesSet.containsValue(index)) {
|
||||
this.attributes.add(index);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
this.attributes = new IntHashSet(attributes, false);
|
||||
}//else//
|
||||
}//EntityMetadata()//
|
||||
/**
|
||||
* EntityMetadata constructor.
|
||||
* @param type The entity class that the metadata applies to.
|
||||
* @param attributes The attributes that will be included or excluded.
|
||||
* @param inclusive Whether the given attributes are to be included. If false, the metadata user will exclude the attributes passed and include all other attributtes for the type passed, (any attributes not explicitly excluded).
|
||||
*/
|
||||
public EntityMetadata(Class type, Attribute[] attributes, boolean inclusive) {
|
||||
super();
|
||||
|
||||
if(!IEntity.class.isAssignableFrom(type) || attributes == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}//if//
|
||||
|
||||
this.type = type;
|
||||
|
||||
//Convert the exclusive attributes array into an inclusive one.//
|
||||
if(!inclusive) {
|
||||
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
int totalCount = metadata.getLastAttributeNumber();
|
||||
IntHashSet attributesSet = new IntHashSet(Attribute.convert(attributes), false);
|
||||
|
||||
this.attributes = new IntHashSet(totalCount - attributes.length);
|
||||
|
||||
for(int index = 0; index <= totalCount; index++) {
|
||||
if(!attributesSet.containsValue(index)) {
|
||||
this.attributes.add(index);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
this.attributes = new IntHashSet(Attribute.convert(attributes), false);
|
||||
}//else//
|
||||
}//EntityMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.IMetadata#getType()
|
||||
*/
|
||||
public Class getType() {
|
||||
return type;
|
||||
}//getType()//
|
||||
/**
|
||||
* Gets the attributes of the IEntity that are to be included.
|
||||
* @return The collection of attributes that make up the metadata.
|
||||
*/
|
||||
public IntHashSet getAttributes() {
|
||||
return attributes;
|
||||
}//getAttributes()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
int count = 0;
|
||||
|
||||
/*byte version = */in.readByte();
|
||||
type = (Class) in.readObject();
|
||||
count = in.readInt();
|
||||
attributes = new IntHashSet(count);
|
||||
|
||||
for(int index = 0; index < count; index++) {
|
||||
attributes.add(metadata.getAttributeNumber(in.readUTF()));
|
||||
}//for//
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
int count = 0;
|
||||
|
||||
/*byte version = */in.readByte();
|
||||
type = (Class) in.readObject();
|
||||
count = in.readInt();
|
||||
attributes = new IntHashSet(count);
|
||||
|
||||
for(int index = 0; index < count; index++) {
|
||||
attributes.add(metadata.getAttributeNumber(in.readUTF()));
|
||||
}//for//
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
IList attributeNames = MetadataService.getSingleton().getTypeMetadata(type).getAttributeNames();
|
||||
IIntIterator iterator = attributes.iterator();
|
||||
|
||||
out.writeByte(0);
|
||||
out.writeObject(type);
|
||||
out.writeInt(attributes.getSize());
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
out.writeUTF((String) attributeNames.get(iterator.next()));
|
||||
}//while//
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
IList attributeNames = MetadataService.getSingleton().getTypeMetadata(type).getAttributeNames();
|
||||
IIntIterator iterator = attributes.iterator();
|
||||
|
||||
out.writeByte(0);
|
||||
out.writeObject(type);
|
||||
out.writeInt(attributes.getSize());
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
out.writeUTF((String) attributeNames.get(iterator.next()));
|
||||
}//while//
|
||||
}//writeExternal()//
|
||||
}//TypeMetadata//
|
||||
288
Foundation/src/com/foundation/common/IEntity.java
Normal file
288
Foundation/src/com/foundation/common/IEntity.java
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.common;
|
||||
|
||||
import java.io.Externalizable;
|
||||
|
||||
import com.common.comparison.*;
|
||||
import com.common.io.IExternalizable;
|
||||
import com.foundation.attribute.AttributeSupport;
|
||||
import com.foundation.attribute.IReflectableObject;
|
||||
import com.foundation.application.IApplication;
|
||||
import com.foundation.event.*;
|
||||
import com.foundation.event.model.IModelListenerAttributeHandler;
|
||||
import com.foundation.event.model.IModelListenerCollectionHandler;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.metadata.Event;
|
||||
import com.foundation.transaction.TransactionContextHolder;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.transaction.Transaction;
|
||||
import com.foundation.transaction.TransactionService;
|
||||
|
||||
public interface IEntity extends IReflectableObject, IEventListener, IEventEmitter, IComparable, IExternalizable, Externalizable {
|
||||
public static final Event VIRTUAL_OBJECT_CHANGED = AttributeSupport.registerEvent(IEntity.class, "virtualObjectChanged");
|
||||
|
||||
/**
|
||||
* This interface is to be passed to the calculatedAttributeRegister method so the framework can capture when calculated attributes require updating.
|
||||
*/
|
||||
public static interface ICalculatedAttributeListener extends IModelListenerCollectionHandler, IModelListenerAttributeHandler {
|
||||
}//ICalculatedAttributeListener//
|
||||
|
||||
/** Indicates that no error occured in the model. Check for a transaction error code. */
|
||||
public static final int MODEL_ERROR_NONE = 0;
|
||||
/** An unexpected model error occured. */
|
||||
public static final int MODEL_ERROR_UNKOWN_ERROR = 1;
|
||||
/** The model version in memory did not match the version of the data in the repository. An update of the in memory model must be performed, and the synchronizing code must wait for updates. */
|
||||
public static final int MODEL_ERROR_BAD_VERSION = 2;
|
||||
/** Preparations for the transaction failed. */
|
||||
public static final int MODEL_ERROR_FAILED_PREPERATIONS = 3;
|
||||
/** The model's key could not be created. This almost always is due to failing to mark the proper attributes in the entity(s) as key using IEntity.AO_KEY. */
|
||||
public static final int MODEL_ERROR_KEY_CREATION_FAILED = 4;
|
||||
|
||||
/** The identifier for the reusable object returned from a transaction where the entity's repository version didn't match the instance's version (requiring updates from the repository before commiting any changes). */
|
||||
public static final TransactionErrorInfo ENTITY_TRANSACTION_BAD_VERSION = new TransactionErrorInfo(Transaction.ERROR_NONE, MODEL_ERROR_BAD_VERSION);
|
||||
/** The identifier for the reusable object returned from a failed transation where failure was due to transaction preparations (not in the actual transaction code). */
|
||||
public static final TransactionErrorInfo ENTITY_TRANSACTION_FAILED_PREPERATIONS = new TransactionErrorInfo(Transaction.ERROR_NONE, MODEL_ERROR_FAILED_PREPERATIONS);
|
||||
/** The identifier for the reusable object returned from a failed transation where the failure reason due to a problem commiting the transaction. */
|
||||
public static final TransactionErrorInfo ENTITY_TRANSACTION_COMMIT_FAILED = new TransactionErrorInfo(Transaction.ERROR_COMMIT_FAILED, MODEL_ERROR_NONE);
|
||||
/** The identifier for the reusable object returned from a failed transation where the failure reason is unknown. */
|
||||
public static final TransactionErrorInfo ENTITY_TRANSACTION_UNKOWN_ERROR = new TransactionErrorInfo(Transaction.ERROR_NONE, MODEL_ERROR_UNKOWN_ERROR);
|
||||
/**
|
||||
* Gets the application object that this object is running under. The application object provides the object various services that it will need.
|
||||
* @return The application reference for the application this object is running under.
|
||||
*/
|
||||
public IApplication getApplication();
|
||||
/**
|
||||
* Gets the support object for the entity's attributes.
|
||||
* @return The attribute support object for the entity.
|
||||
*/
|
||||
public AttributeSupport getAttributeSupport();
|
||||
/**
|
||||
* Gets a key object for this entity instance that is serializeable and hashable. It is recommended that this be used infrequently if possible to avoid excessive overhead since keys are not retained by the entity.
|
||||
* @return The serializable & hashable & comparable (equality only) object that represents this entity.
|
||||
*/
|
||||
public Object entityGetKey();
|
||||
/**
|
||||
* Sets a key object for this entity instance. This fills in the key attributes. This call will fail if any attributes have been set for the instance.
|
||||
* @param key The serializable & hashable & comparable (equality only) object that represents this entity.
|
||||
*/
|
||||
public void entitySetKey(Object key);
|
||||
/**
|
||||
* Gets the number of attributes this entity has declared.
|
||||
* @return The count of entity attributes.
|
||||
*/
|
||||
public int getAttributeCount();
|
||||
/**
|
||||
* Gets the name of the attribute associated with the given attribute number.
|
||||
* <p>This method allows the application easy access to the entity's attribute names using the attribute identifiers.
|
||||
* <code>
|
||||
* ((IEntity) object).getAttributeName(MyEntity.MY_ATTRIBUTE);
|
||||
* </code>
|
||||
* @param attributeNumber The number that was assigned to the desired attribute.
|
||||
* @return The name of the attribute.
|
||||
*/
|
||||
public String getAttributeName(int attributeNumber);
|
||||
/**
|
||||
* 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.
|
||||
* <p>Note: This method can be perticularly useful when synchronizing changes to a repository. The old value may often need to be explicitly deleted from the repository.</p>
|
||||
* @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(Attribute attribute);
|
||||
/**
|
||||
* Determines whether the object is bound (via metadata) to a repository.
|
||||
* @return Whether the object has repository metadata and thus can be stored/retreived to/from a repository.
|
||||
*/
|
||||
public boolean isObjectRepositoryBound();
|
||||
/**
|
||||
* Determines whether the object is not yet stored in a repository.
|
||||
* @return Whether the object has not yet been persisted. This is true if the object was created and not read from a repostiory, or if it has not yet been placed in a repository.
|
||||
*/
|
||||
public boolean isObjectNew();
|
||||
/**
|
||||
* Determines whether any of the object's attributes have been changed since the last time the change flags were reset.
|
||||
* @return Whether any attribute has been given a new value (including null).
|
||||
*/
|
||||
public boolean getIsObjectChanged();
|
||||
/**
|
||||
* Determines whether any of the object's attributes or any attributes of any part-of objects (recursive) have been changed since the last time the change flags were reset.
|
||||
* @return Whether any attribute or any part-of object's attribute has been given a new value (including null).
|
||||
*/
|
||||
public boolean getIsVirtualObjectChanged();
|
||||
/**
|
||||
* Resets all the change flags.
|
||||
*/
|
||||
public void resetObjectChangeFlags();
|
||||
/**
|
||||
* Resets all the change flags in this object and all part-of objects.
|
||||
*/
|
||||
public void resetVirtualObjectChangeFlags();
|
||||
/**
|
||||
* Undoes all changes made to this object since the last time the change flags were reset, or the object was successfully updated in the repository.
|
||||
*/
|
||||
public void reverseObjectChanges();
|
||||
/**
|
||||
* Undoes all changes made to this object since the last time the change flags were reset, or the object was successfully updated in the repository.
|
||||
*/
|
||||
public void reverseVirtualObjectChanges();
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public boolean 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.
|
||||
* </p>
|
||||
*/
|
||||
public void 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.
|
||||
* </p>
|
||||
*/
|
||||
public void resetVirtualObjectAlteredFlags();
|
||||
/**
|
||||
* Gets the entity the entity is part of.
|
||||
* @return The entity that this entity is a part of. This may be null.
|
||||
*/
|
||||
public IEntity getPartOfEntity();
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* Gets the entities' transaction service servicing the application this entity belongs to.
|
||||
* @return The transaction service that can field requests by this entity.
|
||||
*/
|
||||
public TransactionService getTransactionService();
|
||||
/**
|
||||
* Gets the repository identifier used for this entity.
|
||||
* @return The identifier for the repository the entity was read from, or the default repository for this class.
|
||||
*/
|
||||
public Object getRepositoryIdentifier();
|
||||
/**
|
||||
* Gets the repository identifier used for this entity.
|
||||
* @param defaultRepositoryIdentifier The repository identifier to use if the entity isn't already in a repository. If this is null then the application's default repository identifier will be used.
|
||||
* @return The identifier for the repository the entity was read from, or the default repository for this class.
|
||||
*/
|
||||
public Object getRepositoryIdentifier(Object defaultRepositoryIdentifier);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate();
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param debugLevel The TransactionService.DEBUG_xxx identifier specifying the level of debugging for the transaction. This may be -1 to use the default value in the TransactionService.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(int debugLevel);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param updateTask The update task to be run after starting a transaction and validating & locking the version via the repository (if the logical object is bound to a repository that defines version metadata).
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(Runnable updateTask);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param updateTask The update task to be run after starting a transaction and validating & locking the version via the repository (if the logical object is bound to a repository that defines version metadata).
|
||||
* @param debugLevel The TransactionService.DEBUG_xxx identifier specifying the level of debugging for the transaction. This may be -1 to use the default value in the TransactionService.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(Runnable updateTask, int debugLevel);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>Note: This is a method to override for custom processing of repository updates (for example sending the object to a file).</p>
|
||||
* <p>TODO: Support part of a logical object existing in a different repository from the rest of the logical object.</p>
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param updateTask The update task to be run after starting a transaction and validating & locking the version via the repository (if the logical object is bound to a repository that defines version metadata).
|
||||
* @param defaultRepositoryIdentifier The identifier for the repository to use if none of the logical object is in a repository already.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(Runnable updateTask, Object defaultRepositoryIdentifier);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data for the entire logical object.
|
||||
* <p>Note: This is a method to override for custom processing of repository updates (for example sending the object to a file).</p>
|
||||
* <p>TODO: Support part of a logical object existing in a different repository from the rest of the logical object.</p>
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param updateTask The update task to be run after starting a transaction and validating & locking the version via the repository (if the logical object is bound to a repository that defines version metadata).
|
||||
* @param defaultRepositoryIdentifier The identifier for the repository to use if none of the logical object is in a repository already.
|
||||
* @param debugLevel The TransactionService.DEBUG_xxx identifier specifying the level of debugging for the transaction. This may be -1 to use the default value in the TransactionService.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(Runnable updateTask, Object defaultRepositoryIdentifier, int debugLevel);
|
||||
/**
|
||||
* Updates the default repository with the latest entity data.
|
||||
* <p>This method may be called if the entity is not in the repository (it was not read from the repository before being edited), or when the entity was read from the repository and then subsiqently edited.</p>
|
||||
* <p><b>This method may only be used to perform single instance updates in the repository.</b></p>
|
||||
* @param context The transactional context within which the update is occuring. All repository interactions must be through this context so that they can roll back or commit as one unit.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityUpdate(TransactionContextHolder context);
|
||||
/**
|
||||
* Deletes the object in the default repository.
|
||||
* <p><b>This method may only be used to perform single instance deletions in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityDelete();
|
||||
/**
|
||||
* Deletes the object in the default repository.
|
||||
* <p><b>This method may only be used to perform single instance deletions in the repository.</b></p>
|
||||
* <p><b>It is the caller's responsibility to lock this object's monitor before calling this method.</b></p>
|
||||
* @param debugLevel The TransactionService.DEBUG_xxx identifier specifying the level of debugging for the transaction. This may be -1 to use the default value in the TransactionService.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityDelete(int debugLevel);
|
||||
/**
|
||||
* Deletes the object in the default repository.
|
||||
* <p><b>This method may only be used to perform single instance deletions in the repository.</b></p>
|
||||
* @param context The transactional context within which the delete is occuring. All repository interactions must be through this context so that they can roll back or commit as one unit.
|
||||
* @return The result of the operation. A null value indicates success. The method will report success if the transaction was unnecessary.
|
||||
*/
|
||||
public TransactionErrorInfo entityDelete(TransactionContextHolder context);
|
||||
/**
|
||||
* Determines whether the model should use repository version tracking which allows multiple instances of the same model to exist since the repository will perform the version tracking instead of the model instance.
|
||||
* @return Whether the model instance defers the version tracking to the repository. This is setup in the repository/model mapping metadata defined by the application.
|
||||
*/
|
||||
public boolean useRepositoryVersionTracking();
|
||||
}//IEntity//
|
||||
22
Foundation/src/com/foundation/common/IMetadata.java
Normal file
22
Foundation/src/com/foundation/common/IMetadata.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.common;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import com.common.io.IExternalizable;
|
||||
|
||||
/**
|
||||
* This interface is used by the EntityMetadata and future metadata types that can be passed around to specify what data is required in different situations.
|
||||
*/
|
||||
public interface IMetadata extends IExternalizable, Externalizable {
|
||||
/**
|
||||
* The class of object for which this metadata can be used.
|
||||
* @return The metadata type mapping.
|
||||
*/
|
||||
public Class getType();
|
||||
}//IMetadata//
|
||||
100
Foundation/src/com/foundation/common/MetadataContainer.java
Normal file
100
Foundation/src/com/foundation/common/MetadataContainer.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.common;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.common.util.LiteHashMap;
|
||||
|
||||
/*
|
||||
* This container is used to pass information about what to include or exclude when performing such operations as cloning models.
|
||||
*/
|
||||
public class MetadataContainer implements IExternalizable, Externalizable {
|
||||
/** Tells the user of the metadata to not include any reflections. */
|
||||
public static final int FLAG_EXCLUDE_REFLECTIONS = 1;
|
||||
|
||||
private LiteHashMap metadataByTypeMap = new LiteHashMap(10, Comparator.getLogicalComparator(), null);
|
||||
private int flags = 0;
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
*/
|
||||
public MetadataContainer() {
|
||||
super();
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
* @param flags One or more of the flag identifiers defined by this class.
|
||||
*/
|
||||
public MetadataContainer(int flags) {
|
||||
super();
|
||||
this.flags = flags;
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
* @param metadata The set of metadata instances, one for each type for which metadata is provided. The type must match exactly with the object whose metadata is being searched for (ie: type hierarchy is not considered).
|
||||
*/
|
||||
public MetadataContainer(IMetadata[] metadata) {
|
||||
super();
|
||||
|
||||
for(int index = 0; index < metadata.length; index++) {
|
||||
metadataByTypeMap.put(metadata[index].getType(), metadata[index]);
|
||||
}//for//
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* Gets the metadata for the given type.
|
||||
* @param type The type whose metadata is to be retreived.
|
||||
* @return The metadata for that exact type, or null if none was specified.
|
||||
*/
|
||||
public IMetadata getMetadata(Class type) {
|
||||
return (IMetadata) metadataByTypeMap.get(type);
|
||||
}//getMetadata()//
|
||||
/**
|
||||
* Determines whether reflections should be excluded based on the flags passed to this metadata container.
|
||||
* @return Whether reflections should be excluded.
|
||||
*/
|
||||
public boolean excludeReflections() {
|
||||
return (flags & FLAG_EXCLUDE_REFLECTIONS) > 0;
|
||||
}//excludeReflections()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(metadataByTypeMap);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(metadataByTypeMap);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
metadataByTypeMap = (LiteHashMap) in.readObject();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
metadataByTypeMap = (LiteHashMap) in.readObject();
|
||||
}//readExternal()//
|
||||
}//MetadataContainer//
|
||||
@@ -0,0 +1,797 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.controller;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.foundation.attribute.ReflectionContext;
|
||||
import com.foundation.view.IView;
|
||||
import com.foundation.view.IViewContext;
|
||||
import com.foundation.view.ViewSystemMetadata;
|
||||
|
||||
/**
|
||||
* The base class for the RemoteViewController and the ViewController providing functionality common to both the remote and local views.
|
||||
*/
|
||||
public abstract class AbstractViewController extends Controller {
|
||||
public static final int OPTION_MODELESS = 0;
|
||||
public static final int OPTION_MODAL = 1;
|
||||
public static final int OPTION_APPLICATION_MODAL = 2;
|
||||
public static final int OPTION_SYSTEM_MODAL = 3;
|
||||
|
||||
/** The view controller's current view. */
|
||||
private IView view = null;
|
||||
/** Whether the view has been opened yet. This will stay true indefinately once the view has been opened. */
|
||||
private boolean isOpen = false;
|
||||
/** Whether the view has been closed. This will stay true indefinately once the view is closed. This is not true before the view is opened. */
|
||||
private boolean isClosed = false;
|
||||
/** The view context for the current view. This will be null if the view is null. */
|
||||
private IViewContext context = null;
|
||||
/** Prevents an opening view from being opened twice. */
|
||||
private boolean isOpening = false;
|
||||
/** The parent view's controller. This will only be set for modal views. */
|
||||
private AbstractViewController parent = null;
|
||||
/** Whether the view is actively in the process of closing. */
|
||||
private volatile boolean isClosing = false;
|
||||
/** The handler called prior to closing the view and which is capable of canceling the closure. */
|
||||
private IVetoableRunnable preCloseHandler = null;
|
||||
/** The handler called when the view closes (before control is returned to the parent view). */
|
||||
private Runnable onCloseHandler = null;
|
||||
/** The handler called when the view disposes (before control is returned to the parent view). */
|
||||
private Runnable onDisposeHandler = null;
|
||||
/** The reflection context for this view. All internal reflections should use this context since the context will be released when the view is closed. */
|
||||
private ReflectionManager reflectionManager = null;
|
||||
/** The options for this view. Currently this is either OPTION_MODAL or OPTION_MODELESS. */
|
||||
private int options = OPTION_MODAL;
|
||||
/** The parent component assigned when the view controller is controlling only part of a view. */
|
||||
private IView parentComponent = null;
|
||||
/** The manager used to control the decorations. */
|
||||
private DecorationManager decorationManager = new DecorationManager();
|
||||
/** Whether to validate the view upon opening it. */
|
||||
private boolean validateOnOpen = false;
|
||||
/**
|
||||
* AbstractViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
*/
|
||||
AbstractViewController(IViewContext context) {
|
||||
this(context, false);
|
||||
}//AbstractViewController()//
|
||||
/**
|
||||
* AbstractViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
*/
|
||||
AbstractViewController(IViewContext context, ReflectionContext reflectionContext) {
|
||||
this(context, reflectionContext, false);
|
||||
}//AbstractViewController()//
|
||||
/**
|
||||
* AbstractViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is false by default.
|
||||
*/
|
||||
AbstractViewController(IViewContext context, boolean validateOnOpen) {
|
||||
this.validateOnOpen = validateOnOpen;
|
||||
|
||||
if(context == null) {
|
||||
throw new IllegalArgumentException("A view context must be provided to the view.");
|
||||
}//if//
|
||||
|
||||
this.context = context;
|
||||
this.reflectionManager = new ReflectionManager(this, new ReflectionContext(context.getRequestHandler(), getViewClass(), new Runnable() {
|
||||
public void run() {
|
||||
validate();
|
||||
}//run()//
|
||||
}));
|
||||
}//AbstractViewController()//
|
||||
/**
|
||||
* AbstractViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is false by default.
|
||||
*/
|
||||
AbstractViewController(IViewContext context, ReflectionContext reflectionContext, boolean validateOnOpen) {
|
||||
this.validateOnOpen = validateOnOpen;
|
||||
|
||||
if(context == null) {
|
||||
throw new IllegalArgumentException("A view context must be provided to the view.");
|
||||
}//if//
|
||||
|
||||
if((reflectionContext != null) && (reflectionContext.getRequestHandler() != context.getRequestHandler())) {
|
||||
Debug.log(new RuntimeException(), "Error: Can't use the reflection context since it uses a different request handler from the view context.");
|
||||
reflectionContext = null;
|
||||
}//if//
|
||||
|
||||
this.context = context;
|
||||
|
||||
if(reflectionContext == null) {
|
||||
this.reflectionManager = new ReflectionManager(this, new ReflectionContext(context.getRequestHandler(), getViewClass(), new Runnable() {
|
||||
public void run() {
|
||||
validate();
|
||||
}//run()//
|
||||
}));
|
||||
}//if//
|
||||
else {
|
||||
this.reflectionManager = new ReflectionManager(this, reflectionContext);
|
||||
}//else//
|
||||
}//AbstractViewController()//
|
||||
/**
|
||||
* Gets the decoration manager for this view.
|
||||
* @return The view's decoration manager.
|
||||
*/
|
||||
public DecorationManager getDecorationManager() {
|
||||
return decorationManager;
|
||||
}//getDecorationManager()//
|
||||
/**
|
||||
* Closes the view if it is open.
|
||||
* All waiting threads will be notified after the view has been destroyed.
|
||||
* <p> This method may be called by any thread. </p>
|
||||
* @return Whether the close was actually performed.
|
||||
*/
|
||||
public final boolean close() {
|
||||
boolean result = false;
|
||||
boolean isOpened;
|
||||
boolean isClosed;
|
||||
boolean isClosing;
|
||||
|
||||
synchronized(this) {
|
||||
isOpened = this.isOpen;
|
||||
isClosed = this.isClosed;
|
||||
isClosing = this.isClosing;
|
||||
|
||||
if(isOpened && !isClosing && !isClosed) {
|
||||
this.isClosing = true;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
|
||||
//Prevent closing twice or calling it recursively.//
|
||||
if(isOpened && !isClosed && !isClosing) {
|
||||
final IView view;
|
||||
final Runnable onCloseHandler;
|
||||
final Runnable onDisposeHandler;
|
||||
boolean doClose = true;
|
||||
|
||||
try {
|
||||
if(preCloseHandler != null) {
|
||||
doClose = preCloseHandler.run();
|
||||
}//if//
|
||||
|
||||
if(doClose) {
|
||||
result = true;
|
||||
preClose();
|
||||
|
||||
synchronized(this) {
|
||||
if(this.view != null) {
|
||||
onCloseHandler = getOnCloseHandler();
|
||||
onDisposeHandler = getOnDisposeHandler();
|
||||
view = this.view;
|
||||
setOnCloseHandler(null);
|
||||
this.view = null;
|
||||
this.isClosed = true;
|
||||
}//if//
|
||||
else {
|
||||
view = null;
|
||||
onCloseHandler = null;
|
||||
onDisposeHandler = null;
|
||||
}//else//
|
||||
|
||||
//Unregister all reflections. Failure to do so will result in memory leaks, perticularly if the data is accessed over a network.//
|
||||
getReflectionManager().release();
|
||||
}//synchronized//
|
||||
|
||||
if(view != null) {
|
||||
//Note: Moved this out of the threaded code since we should always be calling this on the SWT event thread.//
|
||||
internalClose(view, onCloseHandler, onDisposeHandler);
|
||||
|
||||
synchronized(this) {
|
||||
context = null;
|
||||
notifyAll();
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
synchronized(this) {
|
||||
this.isClosing = false;
|
||||
}//synchronized//
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//close()//
|
||||
/*
|
||||
* TODO: The problem with this idea (to allow reusable partial view's) is that the reflection manager must be released sometime, but can't be released now, so when?
|
||||
* Closes the view if it is open.
|
||||
* All waiting threads will be notified after the view has been destroyed.
|
||||
* <p> This method is only to be called by the caller of openPartial. </p>
|
||||
*
|
||||
public final void closePartial(IView view) {
|
||||
//final IView view;
|
||||
final Runnable onCloseHandler;
|
||||
|
||||
synchronized(this) {
|
||||
if(this.view != null) {
|
||||
onCloseHandler = getOnCloseHandler();
|
||||
//view = this.view;
|
||||
//setOnCloseHandler(null);
|
||||
this.view = null;
|
||||
}//if//
|
||||
else {
|
||||
view = null;
|
||||
//onCloseHandler = null;
|
||||
}//else//
|
||||
|
||||
//Unregister all reflections. Failure to do so will result in memory leaks, perticularly if the data is accessed over a network.//
|
||||
getReflectionManager().release();
|
||||
getReflectionManager().getReflectionContext().release();
|
||||
}//synchronized//
|
||||
|
||||
if(view != null) {
|
||||
//Ensure that the close occurs on the proper thread.//
|
||||
//Note: Don't call the execute method since the view reference is no longer valid.//
|
||||
view.execute(new IRunnable() {
|
||||
public Object run() {
|
||||
internalClose(view, onCloseHandler);
|
||||
|
||||
return null;
|
||||
}//run()//
|
||||
});
|
||||
|
||||
synchronized(this) {
|
||||
context = null;
|
||||
notifyAll();
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//close()//
|
||||
*/
|
||||
/**
|
||||
* Creates the controller's primary view component.
|
||||
* @return This view controller's new view object.
|
||||
*/
|
||||
protected abstract IView createView();
|
||||
/**
|
||||
* Gets the controller's view's class.
|
||||
* @return The class identifying the view code for this controller.
|
||||
*/
|
||||
protected abstract Class getViewClass();
|
||||
/**
|
||||
* Requests that the view validate all the data (perticularly the altered reflection data when using reflections).
|
||||
* <p>Note: This method is automatically called when synchronizing one or more altered reflections without passing a validation handler.</p>
|
||||
* @return Whether the data was validated.
|
||||
*/
|
||||
public abstract boolean validate();
|
||||
/**
|
||||
* Disables the view associated with this controller.
|
||||
* <p>NOTE: This does not close the view.
|
||||
* @see #close()
|
||||
*/
|
||||
public synchronized void disable() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
view.setIsEnabled(false);
|
||||
}//if//
|
||||
}//disable()//
|
||||
/**
|
||||
* Enables the view associated with this controller so that it is usable by the user.
|
||||
*/
|
||||
public synchronized void enable() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
view.setIsEnabled(true);
|
||||
}//if//
|
||||
}//enable()//
|
||||
/**
|
||||
* Executes the runnable synchronously.
|
||||
* <p>This is a thread safe call.</p>
|
||||
* @param runnable The code block to execute.
|
||||
* @return The runnable's result.
|
||||
* @throws ViewClosedException If the view was closed prior to the call executing.
|
||||
*/
|
||||
public Object execute(IRunnable runnable) throws ViewClosedException {
|
||||
IView view = null;
|
||||
|
||||
synchronized(this) {
|
||||
view = getView();
|
||||
}//synchronized//
|
||||
|
||||
if(view == null) {
|
||||
throw new ViewClosedException();
|
||||
}//if//
|
||||
|
||||
return getView().execute(runnable);
|
||||
}//execute()//
|
||||
/**
|
||||
* @deprecated Use executeAsync(IRunnable) instead.
|
||||
* Do not use this method any more since it is not thead safe.
|
||||
*/
|
||||
public void executeAsynch(IRunnable runnable) {
|
||||
getView().executeAsync(runnable);
|
||||
}//executeAsynch()//
|
||||
/**
|
||||
* Executes the runnable asynchronously.
|
||||
* <p>This is a thread safe call.</p>
|
||||
* @param runnable The code block to execute.
|
||||
* @throws ViewClosedException If the view was closed prior to the call executing.
|
||||
*/
|
||||
public void executeAsync(IRunnable runnable) throws ViewClosedException {
|
||||
synchronized(this) {
|
||||
if(getView() != null) {
|
||||
getView().executeAsync(runnable);
|
||||
}//if//
|
||||
else {
|
||||
throw new ViewClosedException();
|
||||
}//else//
|
||||
}//synchronized//
|
||||
}//executeAsynch()//
|
||||
/**
|
||||
* Called by the GC thread upon destroying this object.
|
||||
*/
|
||||
protected void finalize() throws Throwable {
|
||||
//Just in case we will unregister all reflections one more time. This should have already occured when the view closed.//
|
||||
getReflectionManager().release();
|
||||
}//finalize()//
|
||||
/**
|
||||
* Gets the view context provided when the view was opened.
|
||||
* Each view should have a context (view framework dependant) which can be created from any other view controller's context.
|
||||
* <p>Note: Views exist under the same context for the same client (or in the case of the thick client views, they all exist under the same context).
|
||||
* This is not a view specific context, but rather a view system context, one for each view system.</p>
|
||||
* @return The view framework dependant view context.
|
||||
*/
|
||||
public final IViewContext getContext() {
|
||||
return context;
|
||||
}//getContext()//
|
||||
/**
|
||||
* Gets the handler run before the view is closed.
|
||||
* @return The handler to be run just after before hiding or closing the view.
|
||||
*/
|
||||
public IVetoableRunnable getPreCloseHandler() {
|
||||
return preCloseHandler;
|
||||
}//getPreCloseHandler()//
|
||||
/**
|
||||
* Gets the handler run just before the view is closed.
|
||||
* @return The handler to be run just after hiding the view and before closing the view.
|
||||
*/
|
||||
public Runnable getOnCloseHandler() {
|
||||
return onCloseHandler;
|
||||
}//getOnCloseHandler()//
|
||||
/**
|
||||
* Gets the handler run just after the view is disposed.
|
||||
* @return The handler to be run just after the view has been completely destroyed.
|
||||
*/
|
||||
public Runnable getOnDisposeHandler() {
|
||||
return onDisposeHandler;
|
||||
}//getOnDisposeHandler()//
|
||||
/**
|
||||
* Gets the options specified when opening the view.
|
||||
* @return The options given when the view's controller was opened.
|
||||
* @see #OPTION_MODELESS
|
||||
* @see #OPTION_MODAL
|
||||
* @see #OPTION_APPLICATION_MODAL
|
||||
* @see #OPTION_SYSTEM_MODAL
|
||||
*/
|
||||
public final int getOptions() {
|
||||
return options;
|
||||
}//getOptions()//
|
||||
/**
|
||||
* Gets the parent view controller if one was supplied durring the opening of the view controller.
|
||||
* @return This view's parent view's controller object. This value will be null for partial views (embedded in another view), or for modeless views.
|
||||
*/
|
||||
public final AbstractViewController getParent() {
|
||||
return parent;
|
||||
}//getParent()//
|
||||
/**
|
||||
* Gets the view's parent view component. This will only be available if the view is an integrated component in another view.
|
||||
* @return The view component that this view controller's view is part of. For standalone view's, this value will be null.
|
||||
*/
|
||||
public final IView getParentComponent() {
|
||||
return parentComponent;
|
||||
}//getParentComponent()//
|
||||
/**
|
||||
* Sets the view's parent view component. This will only be available if the view is an integrated component in another view.
|
||||
* @param parentComponent The view component that this view controller's view is part of. For standalone view's, this value will be null.
|
||||
*/
|
||||
public final void setParentComponent(IView parentComponent) {
|
||||
this.parentComponent = parentComponent;
|
||||
}//setParentComponent()//
|
||||
/**
|
||||
* Gets the reflection manager for this view.
|
||||
* Each view has its own reflection manager which can be called upon to create, synchronize, and release reflections.
|
||||
* <p>Note: Currently the reflection manager is not usable after the view closes. Subsiquently all reflections are also may be unusable (will still be useable if the reflection context was shared and is still accessed by other views). They still exist, but are no longer connected to their reflections. Using them may cause problems since they may be missing unloaded attribute data.</p>
|
||||
* @return The view's reflection manager.
|
||||
*/
|
||||
public final ReflectionManager getReflectionManager() {
|
||||
return reflectionManager;
|
||||
}//getReflectionManager()//
|
||||
/**
|
||||
* Gets the associated view for this view controller. If there isn't yet a view (or the close() method was called and the view was disposed) then this method will return null.
|
||||
* @return Will be the view associated with the controller if the controller has been opened (and not subsquently closed), or getView(true) has been called.
|
||||
* @see #open()
|
||||
* @see #close()
|
||||
*/
|
||||
public final IView getView() {
|
||||
return view;
|
||||
}//getView()//
|
||||
/**
|
||||
* Hides the view associated with this controller.
|
||||
* <p>NOTE: This does not close the view.</p>
|
||||
* @see #close()
|
||||
*/
|
||||
public synchronized void hide() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
view.setIsVisible(false);
|
||||
}//if//
|
||||
}//hide()//
|
||||
/**
|
||||
* Closes the view and releases all components.
|
||||
* <p>Subclasses should override the preClose() and postClose() methods, or set the on close handler.</p>
|
||||
* @param view The view associated with the view controller.
|
||||
* @param onCloseHandler The handler to be called right before closing the view. This is normally set by the opener of the view to handle post processing of the view data.
|
||||
* @param onDisposeHandler The handler to be called right after disposing the view.
|
||||
* @see #preClose()
|
||||
* @see #postClose()
|
||||
* @see #setOnCloseHandler(Runnable)
|
||||
*/
|
||||
protected void internalClose(IView view, Runnable onCloseHandler, Runnable onDispose) {
|
||||
if(view != null) {
|
||||
view.setIsVisible(false);
|
||||
|
||||
//Run the on close handler.//
|
||||
if(onCloseHandler != null) {
|
||||
onCloseHandler.run();
|
||||
}//if//
|
||||
|
||||
view.viewReleaseAll();
|
||||
postClose();
|
||||
|
||||
//Run the on dispose handler.//
|
||||
if(onDispose != null) {
|
||||
onDispose.run();
|
||||
}//if//
|
||||
}//if//
|
||||
}//internalClose()//
|
||||
/**
|
||||
* Opens the view either by displaying the existing view, or by creating a new view.
|
||||
* <p>This method allows subclasses to override the way the newly created view is initialized.</p>
|
||||
* @return The open and visible view.
|
||||
*/
|
||||
protected IView internalInitialize() {
|
||||
return createView();
|
||||
}//internalInitialize()//
|
||||
/**
|
||||
* Initializes the view context.
|
||||
* <p>Note: This must get called immediatly AFTER the view has been created & initialized.
|
||||
* @param viewContext The context the view is being opened under.
|
||||
* @return The view context to be used by this view controller.
|
||||
*/
|
||||
protected void internalInitializeContext(IViewContext viewContext) {
|
||||
}//internalInitializeContext()//
|
||||
/**
|
||||
* Opens the view either by displaying the existing view, or by creating a new view.
|
||||
* <p>This method allows subclasses to override the way the newly created view is initialized.</p>
|
||||
* @return The open and visible view.
|
||||
*/
|
||||
protected void internalOpen(final IView view) {
|
||||
view.suspendLayouts();
|
||||
view.viewInitializeAll();
|
||||
view.resumeLayouts();
|
||||
view.layout();
|
||||
view.pack();
|
||||
postOpenInitialization(view);
|
||||
|
||||
if(validateOnOpen) {
|
||||
validate();
|
||||
}//if//
|
||||
}//internalOpen()//
|
||||
/**
|
||||
* Determines whether the view should validate upon opening. This is normally false.
|
||||
* @return Whether the view validates as soon as it is displayed.
|
||||
*/
|
||||
protected boolean validateOnOpen() {
|
||||
return validateOnOpen;
|
||||
}//validateOnOpen()//
|
||||
/**
|
||||
* Determines whether the view is open.
|
||||
* Note that this is different than isClosed since it reports whether the view has been opened, though it could be subsiquently closed and this method will still report true.
|
||||
* @return Whether the view is currently open (not necessarily visible or enabled).
|
||||
*/
|
||||
public synchronized boolean isOpen() {
|
||||
return isOpen;
|
||||
}//isOpen()//
|
||||
/**
|
||||
* Determines whether the view is open.
|
||||
* Note that this is different than isClosed since it reports whether the view has been opened, though it could be subsiquently closed and this method will still report true.
|
||||
* <p>Note: Not synchronized since it is generally set only while opening the view and synchronization is not necessary.</p>
|
||||
* @param isOpen Whether the view is currently open (not necessarily visible or enabled).
|
||||
*/
|
||||
protected void isOpen(boolean isOpen) {
|
||||
this.isOpen = isOpen;
|
||||
}//isOpen()//
|
||||
/**
|
||||
* Determines whether the view has been closed.
|
||||
* Note that this is different than isOpen since the view must be first opened to be then closed.
|
||||
* @return Whether the view is currently closed.
|
||||
*/
|
||||
public synchronized boolean isClosed() {
|
||||
return isClosed;
|
||||
}//isClosed()//
|
||||
/**
|
||||
* Creates and displays the modal view associated with this controller. A modal view is one which must be closed before the user may interact with any parent view.
|
||||
* The user should close the view by calling the close method.
|
||||
* <p>Note: The thread calling this method will return immediatly and will NOT block until the view is closed.</p>
|
||||
* @param parent The view controller associated with the parent view.
|
||||
* @see #show()
|
||||
* @see #close()
|
||||
*/
|
||||
public void open(AbstractViewController parent) {
|
||||
open(parent, -1, parent != null ? OPTION_MODAL : OPTION_MODELESS);
|
||||
}//open()//
|
||||
/**
|
||||
* Creates and displays the view associated with this controller.
|
||||
* The user should close the view by calling the close method.
|
||||
* <p><b>Warning: The caller should not block unless it is certain that the calling thread is not the message handler in a single threaded view environment. In such a case a handler should be provided to handle the view closure.</b></p>
|
||||
* @param parent The optional parent view controller.
|
||||
* @param timeout The amount of time to wait for the view to close before returning control. This should be zero if the thread should wait for ever. This should be less than zero if there should be no wait.
|
||||
* @param options The options used to open the view. Currently you can pass OPTION_MODELESS or OPTION_MODAL.
|
||||
* @see #show()
|
||||
* @see #close()
|
||||
*/
|
||||
protected final void open(AbstractViewController parent, long timeout, int options) {
|
||||
this.parent = parent;
|
||||
|
||||
open(timeout, options);
|
||||
}//open()//
|
||||
/**
|
||||
* Creates and displays the modeless view associated with this controller. A modeless view is one that does not exist within the life of a parent view. Any parent may close without affecting this view.
|
||||
* The user should close the view by calling the close method.
|
||||
* <p>Note: The thread calling this method will return immediatly and will NOT block until the view is closed.</p>
|
||||
* @see #show()
|
||||
* @see #close()
|
||||
*/
|
||||
public void open() {
|
||||
open(-1, OPTION_MODELESS);
|
||||
}//open()//
|
||||
/**
|
||||
* Creates and displays the view associated with this controller.
|
||||
* The user should close the view by calling the close method.
|
||||
* <p><b>Warning: The caller should not block unless it is certain that the calling thread is not the message handler in a single threaded view environment. In such a case a handler should be provided to handle the view closure.</b></p>
|
||||
* @param timeout The amount of time to wait for the view to close before returning control. This should be zero if the thread should wait for ever. This should be less than zero if there should be no wait.
|
||||
* @param options The options used to open the view. Currently you can pass OPTION_MODELESS or OPTION_MODAL.
|
||||
* @see #show()
|
||||
* @see #close()
|
||||
*/
|
||||
protected final void open(long timeout, int options) {
|
||||
try {
|
||||
//Since we don't want to be synchronized on the view controller while we are opening, we will just synchronize to check the open status.//
|
||||
synchronized(this) {
|
||||
if(view != null) {
|
||||
show();
|
||||
return;
|
||||
}//if//
|
||||
else if(isOpening) {
|
||||
return;
|
||||
}//else if//
|
||||
else {
|
||||
isOpening = true;
|
||||
}//else//
|
||||
}//synchronized//
|
||||
|
||||
this.parentComponent = null;
|
||||
this.options = options;
|
||||
internalInitializeContext(context);
|
||||
this.view = internalInitialize();
|
||||
decorationManager.initialize(view);
|
||||
this.isOpen = true;
|
||||
internalOpen(this.view);
|
||||
|
||||
//Warning: This should not be called if this is the one and only view event handler thread!//
|
||||
if(timeout >= 0) {
|
||||
waitForViewClosure(timeout);
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
synchronized(this) {
|
||||
isOpening = false;
|
||||
}//synchronized//
|
||||
}//finally//
|
||||
}//open()//
|
||||
/**
|
||||
* Opens a partial view given the view's parent view component reference and an optional view context.
|
||||
* <p>This is intended only to be called by the containing view when it requires the sub-component to be created.</p>
|
||||
* <p><b>Warning: This method is not thread safe. It is assumed that the view will be owned by one parent component and will be opened only by that component.</b></p>
|
||||
* @param parent The non-null parent view component passed by the view.
|
||||
* @param context The context for the view. The actual value is dependant upon the view framework being used. In a remote view this value may be the session context for example.
|
||||
* @return A view associated with this view controller that is a part of another view.
|
||||
*/
|
||||
public IView openPartial(IView parent, Object context) {
|
||||
IView result = null;
|
||||
|
||||
if(parent == null) {
|
||||
throw new RuntimeException("Error: Cannot open a partial view without a parent view part.");
|
||||
}//if//
|
||||
else if(context != this.context) {
|
||||
throw new RuntimeException("Error: Invalid view context. This view was initialized with a different context than it is being used under.");
|
||||
}//else if//
|
||||
|
||||
if(view == null) {
|
||||
this.options = 0;
|
||||
this.parentComponent = parent;
|
||||
this.view = createView();
|
||||
decorationManager.initialize(view);
|
||||
this.isOpen = true;
|
||||
this.view.setController(this);
|
||||
this.view.viewInitializeAll();
|
||||
this.view.layout();
|
||||
this.view.pack();
|
||||
postOpenInitialization(this.view);
|
||||
|
||||
if(validateOnOpen) {
|
||||
validate();
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
result = view;
|
||||
|
||||
return result;
|
||||
}//openPartialView()//
|
||||
/**
|
||||
* Called just after closing the view. Note that the reflection context will have been released and any reflections will not be useable (they won't return the correct hashCode, won't be able to load attributes, and will behave as non-reflected objects).
|
||||
* <p><b>Warning: If using a ModelListener, do not release it in this method. Instead use preClose() to release the model listener.</b></p>
|
||||
* <p>Note: This method will always be called by the view thread and can access non-multi-thread safe objects.</p>
|
||||
* @see #preClose()
|
||||
* @see #setOnCloseHandler(Runnable)
|
||||
*/
|
||||
protected void postClose() {
|
||||
}//postClose()//
|
||||
/**
|
||||
* Called after the view is opened but not yet visible to the user.
|
||||
* <p>This method allows the view controller an opportunity to modify how or whether the view is made visible to the user. The default behavior is to make the view visible and to give it the focus. Subclasses can also overload this to position the view.</p>
|
||||
* @param view The newly opened but not yet visible view.
|
||||
*/
|
||||
protected void postOpenInitialization(IView view) {
|
||||
view.setIsVisible(true);
|
||||
view.setFocus();
|
||||
}//postOpenInitialization()//
|
||||
/**
|
||||
* Called just prior to releasing the reflection context and calling the onCloseHandler and closing the view. This is called after calling the preCloseHandler which can veto the closure.
|
||||
* <p><b>Note: This is a good place to release any ModelListeners or other custom listeners that were setup by the view controller.</b></p>
|
||||
* <p>Warning: This method may not be called by the view thread. Implementations should consider this when affecting objects not in a multi-thread safe context. The postClose method is called on the view thread.</p>
|
||||
* @see #postClose()
|
||||
* @see #setOnCloseHandler(Runnable)
|
||||
*/
|
||||
protected void preClose() {
|
||||
}//preClose()//
|
||||
/**
|
||||
* Forces a resfresh of the view (pushing data from the model to the view).
|
||||
*/
|
||||
public synchronized void refresh() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
view.viewRefreshAll();
|
||||
}//if//
|
||||
}//refresh()//
|
||||
/**
|
||||
* Sets the handler run before the view is closed and is intended to allow the view creator to setup post processing of the view data external to the view logic with the ability to cancel the closure if there is a problem.
|
||||
* To setup internal post processing of the view data, the view should override the preClose() or postClose() methods.
|
||||
* Note that only one pre-close handler may be assigned to a view controller. Assigning a second will mean that the first assigned pre-close handler will not be called.
|
||||
* <p><b>Warning: This handler will never be called if the view is opened inside another view (openPartial versus a call to open) since the close cannot be cancelled.</b></p>
|
||||
* <p>This handler can be used to process the view results, interact with the view's reflections before they are disposed, and cancel the closing the view if processing the results fails.</p>
|
||||
* <p><b>Warning: In a single threaded view environment (most of them), the handler will be called by the view's event thread, and thus the handler should do nothing that blocks or takes a long time.</b></p>
|
||||
* @param preCloseHandler The handler to be run before any other close functionality. This handler is can veto the closure process.
|
||||
* @see #preClose()
|
||||
* @see #setOnCloseHandler()
|
||||
* @see #postClose()
|
||||
*/
|
||||
public void setPreCloseHandler(IVetoableRunnable preCloseHandler) {
|
||||
this.preCloseHandler = preCloseHandler;
|
||||
}//setPreCloseHandler()//
|
||||
/**
|
||||
* Sets the handler run just before the view is closed and is intended to allow the view creator to setup post processing of the view data external to the view logic.
|
||||
* To setup internal post processing of the view data, the view should override the preClose() or postClose() methods.
|
||||
* Note that only one on-close handler may be assigned to a view controller. Assigning a second will mean that the first assigned on-close handler will not be called.
|
||||
* <p>This handler can be used to cleanup after the view, and to process the view results if the opening thread is not able to wait for the view to be closed.</p>
|
||||
* <p><b>Warning: In a single threaded view environment (most of them), the handler will be called by the view's event thread, and thus the handler should do nothing that blocks or takes a long time.</b></p>
|
||||
* @param onCloseHandler The handler to be run just after hiding the view and before closing the view. This handler is run between the execution of the preClose() method and the actual view closing.
|
||||
* @see #setPreCloseHandler()
|
||||
* @see #preClose()
|
||||
* @see #postClose()
|
||||
*/
|
||||
public void setOnCloseHandler(Runnable onCloseHandler) {
|
||||
this.onCloseHandler = onCloseHandler;
|
||||
}//setOnCloseHandler()//
|
||||
/**
|
||||
* Sets the handler run just after the view is disposed.
|
||||
* To setup internal post processing of the view data, the view should override the preClose() or postClose() methods.
|
||||
* Note that only one on-dispose handler may be assigned to a view controller. Assigning a second will mean that the first assigned on-dispose handler will not be called.
|
||||
* <p>This handler can be used to cleanup after the view, and to process the view results if the opening thread is not able to wait for the view to be closed.</p>
|
||||
* <p><b>Warning: In a single threaded view environment (most of them), the handler will be called by the view's event thread, and thus the handler should do nothing that blocks or takes a long time.</b></p>
|
||||
* @param onDisposeHandler The handler to be run just after the view is completely disposed.
|
||||
* @see #preClose()
|
||||
* @see #postClose()
|
||||
*/
|
||||
public void setOnDisposeHandler(Runnable onDisposeHandler) {
|
||||
this.onDisposeHandler = onDisposeHandler;
|
||||
}//setOnDisposeHandler()//
|
||||
/**
|
||||
* Sets the options specified when opening the view.
|
||||
* @param options The options given when the view's controller was opened.
|
||||
* @see #OPTION_MODAL
|
||||
* @see #OPTION_MODELESS
|
||||
*/
|
||||
protected void setOptions(int options) {
|
||||
this.options = options;
|
||||
}//setOptions()//
|
||||
/**
|
||||
* Sets the view reference.
|
||||
* @param view The current view for this view controller.
|
||||
*/
|
||||
protected void setView(IView view) {
|
||||
this.view = view;
|
||||
decorationManager.initialize(view);
|
||||
}//setView()//
|
||||
/**
|
||||
* Shows the view associated with this controller so that it is visible to the user.
|
||||
* <p>Note: This does not open the view. The view must first be open.</p>
|
||||
*/
|
||||
public synchronized void show() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
//Set visible and bring to top.//
|
||||
view.show();
|
||||
}//if//
|
||||
}//show()//
|
||||
/**
|
||||
* Forces a synchronize of the view (pushing data from the view to the model).
|
||||
*/
|
||||
public synchronized void synchronize() {
|
||||
IView view = getView();
|
||||
|
||||
if(view != null) {
|
||||
view.viewSynchronizeAll();
|
||||
}//if//
|
||||
}//synchronize()//
|
||||
/**
|
||||
* Waits for the view to be closed before returning.
|
||||
*/
|
||||
public void waitForViewClosure() {
|
||||
waitForViewClosure(0);
|
||||
}//waitForViewClosure()//
|
||||
/**
|
||||
* Waits for the view to be closed or the time to run out before returning.
|
||||
* @param timeout The number of milliseconds to wait before giving up. If this value is zero then the thread may wait forever.
|
||||
*/
|
||||
public synchronized void waitForViewClosure(long timeout) {
|
||||
if(isOpen()) {
|
||||
try {
|
||||
wait(timeout);
|
||||
}//try//
|
||||
catch(InterruptedException e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
}//waitForViewClosure()//
|
||||
/**
|
||||
* Gets the display metadata which describes the view system such as information about the display locations and sizes.
|
||||
* @return The metadata describing the view system.
|
||||
*/
|
||||
public ViewSystemMetadata getViewSystemMetadata() {
|
||||
return getView().getViewContext().getViewSystemMetadata();
|
||||
}//getViewSystemMetadata()//
|
||||
/**
|
||||
* Verifies that the calling thread is allowed access to the view components.
|
||||
* <p>Note: This is really a debug only operation and should be turned off for production applications.</p>
|
||||
*/
|
||||
protected void verifyThread() {
|
||||
if((getView() != null) && (!getView().isViewThread())) {
|
||||
Debug.log(new RuntimeException("Error: Invalid thread."));
|
||||
}//if//
|
||||
}//verifyThread()//
|
||||
}//AbstractViewController()//
|
||||
28
Foundation/src/com/foundation/controller/Controller.java
Normal file
28
Foundation/src/com/foundation/controller/Controller.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2002,2007 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.controller;
|
||||
|
||||
import com.foundation.common.*;
|
||||
|
||||
/*
|
||||
* A controller manages the workflow in the application for a perticular situation.
|
||||
*/
|
||||
public abstract class Controller extends Entity implements IController {
|
||||
/**
|
||||
* Controller constructor.
|
||||
*/
|
||||
public Controller() {
|
||||
super();
|
||||
}//Controller()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IController#getDecorationManager()
|
||||
*/
|
||||
public DecorationManager getDecorationManager() {
|
||||
return null;
|
||||
}//getDecorationManager()//
|
||||
}//Controller//
|
||||
487
Foundation/src/com/foundation/controller/DecorationManager.java
Normal file
487
Foundation/src/com/foundation/controller/DecorationManager.java
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright (c) 2007,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.controller;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.view.AbstractDecoration;
|
||||
import com.foundation.view.IView;
|
||||
|
||||
public class DecorationManager {
|
||||
/** A mapping of DecorationMetadata instances by the object being decorated. The view components get notified of changes to the map and display the appropriate decoration. */
|
||||
private IHashMap decorationMap = new LiteHashMap(60);
|
||||
private IHashMap removedDecorations = new LiteHashMap(60);
|
||||
private IHashMap addedDecorations = new LiteHashMap(60);
|
||||
private IHashSet decorationListeners = new LiteHashSet(100, LiteHashSet.DEFAULT_LOAD_FACTOR, LiteHashSet.DEFAULT_COMPARATOR, LiteHashSet.STYLE_COUNT_DUPLICATES);
|
||||
/** The view the manager can use to add and remove message holds. */
|
||||
private IView view = null;
|
||||
|
||||
private static class DecorationMetadata {
|
||||
private Object object;
|
||||
private Attribute attribute;
|
||||
private AbstractDecoration decoration;
|
||||
private DecorationMetadata next = null;
|
||||
|
||||
/**
|
||||
* DecorationKey constructor.
|
||||
* @param object The object being decorated.
|
||||
* @param attribute The optional attribute to be decorated.
|
||||
* @param decoration The decoration to be applied.
|
||||
*/
|
||||
public DecorationMetadata(Object object, Attribute attribute, AbstractDecoration decoration) {
|
||||
this.object = object;
|
||||
this.attribute = attribute;
|
||||
this.decoration = decoration;
|
||||
}//DecorationMetadata()//
|
||||
/**
|
||||
* Gets the object that is being decorated.
|
||||
* @return The decorated object.
|
||||
*/
|
||||
public Object getObject() {
|
||||
return object;
|
||||
}//getObject()//
|
||||
/**
|
||||
* Gets the attribute being decorated.
|
||||
* @return The decorated attribute, or null if the whole object is decorated.
|
||||
*/
|
||||
public Attribute getAttribute() {
|
||||
return attribute;
|
||||
}//getAttribute()//
|
||||
/**
|
||||
* Gets the decoration being applied.
|
||||
* @return The decoration for the object and attribute.
|
||||
*/
|
||||
public AbstractDecoration getDecoration() {
|
||||
return decoration;
|
||||
}//getDecoration()//
|
||||
/**
|
||||
* Gets the next decoration in the linked list.
|
||||
* @return The decoration metadata that follows this one.
|
||||
*/
|
||||
public DecorationMetadata getNext() {
|
||||
return next;
|
||||
}//getNext()//
|
||||
/**
|
||||
* Sets the next decoration in the linked list.
|
||||
* @param next The decoration metadata that follows this one.
|
||||
*/
|
||||
public void setNext(DecorationMetadata next) {
|
||||
this.next = next;
|
||||
}//setNext()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof DecorationMetadata ? (((DecorationMetadata) object).object == this.object) && (((DecorationMetadata) object).attribute == this.attribute) && (((DecorationMetadata) object).decoration.equals(this.decoration)) : false;
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return object.hashCode() ^ attribute.hashCode();
|
||||
}//hashCode()//
|
||||
}//DecorationMetadata//
|
||||
/**
|
||||
* DecorationManager constructor.
|
||||
*/
|
||||
public DecorationManager() {
|
||||
}//DecorationManager()//
|
||||
/**
|
||||
* Initializes the manager.
|
||||
* @param view The view that the manager can use to add and remove message holds.
|
||||
*/
|
||||
public void initialize(IView view) {
|
||||
this.view = view;
|
||||
}//initialize()//
|
||||
/**
|
||||
* Registers a listener with the view's decorations.
|
||||
* @param listener The listener to be registered.
|
||||
*/
|
||||
public void registerDecorationListener(IDecorationListener listener) {
|
||||
decorationListeners.add(listener);
|
||||
|
||||
//Send the listener any decorations that apply.//
|
||||
for(IIterator iterator = decorationMap.valueIterator(); iterator.hasNext(); ) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if((listener.getDecoratedObject() == decoration.getObject()) && (listener.getDecoratedAttribute() == decoration.getAttribute())) {
|
||||
listener.addDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//for//
|
||||
}//registerDecorationListener()//
|
||||
/**
|
||||
* Unregisters a listener with the view's decorations.
|
||||
* @param listener The listener to be unregistered.
|
||||
*/
|
||||
public void unregisterDecorationListener(IDecorationListener listener, Object oldObject, Attribute oldAttribute) {
|
||||
decorationListeners.remove(listener);
|
||||
|
||||
//Send the listener any decorations that used to apply.//
|
||||
if(oldObject != null) {
|
||||
for(IIterator iterator = decorationMap.valueIterator(); iterator.hasNext(); ) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if((oldObject == decoration.getObject()) && (oldAttribute == decoration.getAttribute())) {
|
||||
listener.removeDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//unregisterDecorationListener()//
|
||||
/**
|
||||
* Registers a listener with the view's decorations.
|
||||
* @param listener The listener to be registered.
|
||||
*/
|
||||
public void registerDecorationListener(IDecorationMultiListener listener) {
|
||||
IIterator iterator = decorationMap.valueIterator();
|
||||
|
||||
decorationListeners.add(listener);
|
||||
|
||||
//Send the listener any decorations that apply.//
|
||||
while(iterator.hasNext()) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if(listener.isListening(decoration.getDecoration().getClass(), decoration.getObject(), decoration.getAttribute())) {
|
||||
listener.addDecoration(decoration.getDecoration(), decoration.getObject(), decoration.getAttribute());
|
||||
}//if//
|
||||
}//while//
|
||||
}//registerDecorationListener()//
|
||||
/**
|
||||
* Unregisters a listener with the view's decorations.
|
||||
* @param listener The listener to be unregistered.
|
||||
*/
|
||||
public void unregisterDecorationListener(IDecorationMultiListener listener) {
|
||||
IIterator iterator = decorationMap.valueIterator();
|
||||
|
||||
decorationListeners.remove(listener);
|
||||
|
||||
//Send the listener any decorations that used to apply.//
|
||||
while(iterator.hasNext()) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if(listener.isListening(decoration.getClass(), decoration.getObject(), decoration.getAttribute())) {
|
||||
listener.removeDecoration(decoration.getDecoration(), decoration.getObject(), decoration.getAttribute());
|
||||
}//if//
|
||||
}//while//
|
||||
}//unregisterDecorationListener()//
|
||||
/**
|
||||
* Notifies the decoration manager that an existing registered listener has changed which object and/or attribute it is interested in.
|
||||
* @param listener The currently registered listener.
|
||||
* @param oldObject The old object that the listener was interested in. This is used to send remove messages to the listener for old decorations.
|
||||
* @param oldAttribute The old attribute that the listener was interested in.
|
||||
*/
|
||||
public void updateDecorationListener(IDecorationListener listener, Object oldObject, Attribute oldAttribute) {
|
||||
//Send the listener any decorations that used to apply.//
|
||||
if(oldObject != null) {
|
||||
for(IIterator iterator = decorationMap.valueIterator(); iterator.hasNext(); ) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if((oldObject == decoration.getObject()) && (oldAttribute == decoration.getAttribute())) {
|
||||
listener.removeDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//Send the listener any decorations that apply.//
|
||||
for(IIterator iterator = decorationMap.valueIterator(); iterator.hasNext(); ) {
|
||||
DecorationMetadata decoration = (DecorationMetadata) iterator.next();
|
||||
|
||||
if((listener.getDecoratedObject() == decoration.getObject()) && (listener.getDecoratedAttribute() == decoration.getAttribute())) {
|
||||
listener.addDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//for//
|
||||
}//updateDecorationListener()//
|
||||
/**
|
||||
* Applies the decoration changes, notifying and updating the view.
|
||||
*/
|
||||
public void applyDecorationChanges() {
|
||||
try {
|
||||
IIterator iterator;
|
||||
|
||||
view.addMessageHold();
|
||||
iterator = removedDecorations.keyIterator();
|
||||
|
||||
//Collect the removed decorations.//
|
||||
while(iterator.hasNext()) {
|
||||
Object object = iterator.next();
|
||||
DecorationMetadata next = (DecorationMetadata) removedDecorations.get(object);
|
||||
|
||||
while(next != null) {
|
||||
internalRemoveDecoration(next);
|
||||
next = next.getNext();
|
||||
}//while//
|
||||
}//while//
|
||||
|
||||
iterator = addedDecorations.valueIterator();
|
||||
|
||||
//Collect the added decorations and also transfer them to the current mapping.//
|
||||
while(iterator.hasNext()) {
|
||||
DecorationMetadata first = (DecorationMetadata) iterator.next();
|
||||
DecorationMetadata next = first;
|
||||
DecorationMetadata last = null;
|
||||
|
||||
while(next != null) {
|
||||
internalAddDecoration(next);
|
||||
last = next;
|
||||
next = next.getNext();
|
||||
}//while//
|
||||
|
||||
//Place the decoration metadata into the current mapping.//
|
||||
last.setNext((DecorationMetadata) decorationMap.get(last.getObject()));
|
||||
decorationMap.put(first.getObject(), first);
|
||||
}//while//
|
||||
|
||||
//Clear the removed and added decoration mappings.//
|
||||
addedDecorations.removeAll();
|
||||
removedDecorations.removeAll();
|
||||
}//try//
|
||||
finally {
|
||||
view.removeMessageHold();
|
||||
}//finally//
|
||||
}//applyDecorationChanges()//
|
||||
/**
|
||||
* Adds the decoration by sending it to the appropriate listeners.
|
||||
* @param decoration The decoration being added.
|
||||
*/
|
||||
protected void internalAddDecoration(DecorationMetadata decoration) {
|
||||
//TODO: Make this more efficient. There could be many many listeners? Determine if there are a lot of listeners for at least a sizeable portion of application views.
|
||||
IIterator iterator = decorationListeners.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
Object next = iterator.next();
|
||||
|
||||
if(next instanceof IDecorationMultiListener) {
|
||||
IDecorationMultiListener listener = (IDecorationMultiListener) next;
|
||||
|
||||
if(listener.isListening(decoration.getDecoration().getClass(), decoration.getObject(), decoration.getAttribute())) {
|
||||
listener.addDecoration(decoration.getDecoration(), decoration.getObject(), decoration.getAttribute());
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
IDecorationListener listener = (IDecorationListener) next;
|
||||
|
||||
if((listener.getDecoratedObject() == decoration.getObject()) && (listener.getDecoratedAttribute() == decoration.getAttribute())) {
|
||||
listener.addDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//else//
|
||||
}//while//
|
||||
}//internalAddDecoration()//
|
||||
/**
|
||||
* Removes the decoration by sending it to the appropriate listeners.
|
||||
* @param decoration The decoration being removed.
|
||||
*/
|
||||
protected void internalRemoveDecoration(DecorationMetadata decoration) {
|
||||
//TODO: Make this more efficient. There could be many many listeners? Determine if there are a lot of listeners for at least a sizeable portion of application views.
|
||||
IIterator iterator = decorationListeners.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
Object next = iterator.next();
|
||||
|
||||
if(next instanceof IDecorationMultiListener) {
|
||||
IDecorationMultiListener listener = (IDecorationMultiListener) next;
|
||||
|
||||
if(listener.isListening(decoration.getDecoration().getClass(), decoration.getObject(), decoration.getAttribute())) {
|
||||
listener.removeDecoration(decoration.getDecoration(), decoration.getObject(), decoration.getAttribute());
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
IDecorationListener listener = (IDecorationListener) next;
|
||||
|
||||
if((listener.getDecoratedObject() == decoration.getObject()) && (listener.getDecoratedAttribute() == decoration.getAttribute())) {
|
||||
listener.removeDecoration(decoration.getDecoration());
|
||||
}//if//
|
||||
}//else//
|
||||
}//while//
|
||||
}//internalRemoveDecoration()//
|
||||
/**
|
||||
* Clears all decorations.
|
||||
* <p>Note: Nothing is actually cleared until the apply method is called.</p>
|
||||
*/
|
||||
public void clearDecorations() {
|
||||
this.clearDecorations(null, null);
|
||||
}//clearDecorations()//
|
||||
/**
|
||||
* Clears all decorations associated with the given object.
|
||||
* @param object The object whose associated decorations should be cleared. If null then all object decorations will be cleared.
|
||||
* @param decorationType The optional decoration type to be cleared. If null then all decoration types will be cleared.
|
||||
*/
|
||||
public void clearDecorations(Object object, Class decorationType) {
|
||||
if(object != null) {
|
||||
//Removes the desired decoration metadata instances and places them in the removed mapping.//
|
||||
if(decorationType != null) {
|
||||
DecorationMetadata current = (DecorationMetadata) decorationMap.get(object);
|
||||
DecorationMetadata previous = null;
|
||||
DecorationMetadata removed = (DecorationMetadata) removedDecorations.get(object);
|
||||
|
||||
while(current != null) {
|
||||
if(decorationType.isAssignableFrom(current.getDecoration().getClass())) {
|
||||
DecorationMetadata next = current.getNext();
|
||||
|
||||
if(previous != null) {
|
||||
previous.setNext(current.getNext());
|
||||
}//if//
|
||||
else {
|
||||
decorationMap.put(object, current.getNext());
|
||||
}//else//
|
||||
|
||||
if(removed == null) {
|
||||
removed = current;
|
||||
removedDecorations.put(object, removed);
|
||||
}//if//
|
||||
else {
|
||||
current.setNext(removed.getNext());
|
||||
removed.setNext(current);
|
||||
}//else//
|
||||
|
||||
current = next;
|
||||
}//if//
|
||||
else {
|
||||
previous = current;
|
||||
current = current.getNext();
|
||||
}//else//
|
||||
}//while//
|
||||
}//if//
|
||||
else {
|
||||
DecorationMetadata removed = (DecorationMetadata) decorationMap.remove(object);
|
||||
|
||||
if(removed != null) {
|
||||
DecorationMetadata next = removed;
|
||||
|
||||
//Locate the last decoration metadata in the linked list.//
|
||||
while(next.getNext() != null) {
|
||||
next = next.getNext();
|
||||
}//while//
|
||||
|
||||
next.setNext((DecorationMetadata) removedDecorations.get(object));
|
||||
removedDecorations.put(object, removed);
|
||||
}//if//
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//Removes the desired decoration metadata instances and places them in the removed mapping.//
|
||||
if(decorationType != null) {
|
||||
IIterator objectIterator = decorationMap.keyIterator();
|
||||
|
||||
while(objectIterator.hasNext()) {
|
||||
DecorationMetadata current;
|
||||
DecorationMetadata previous = null;
|
||||
DecorationMetadata removed;
|
||||
|
||||
object = objectIterator.next();
|
||||
current = (DecorationMetadata) decorationMap.get(object);
|
||||
removed = (DecorationMetadata) removedDecorations.get(object);
|
||||
|
||||
while(current != null) {
|
||||
if(decorationType.isAssignableFrom(current.getDecoration().getClass())) {
|
||||
DecorationMetadata next = current.getNext();
|
||||
|
||||
if(previous != null) {
|
||||
previous.setNext(current.getNext());
|
||||
}//if//
|
||||
else {
|
||||
decorationMap.put(object, current.getNext());
|
||||
}//else//
|
||||
|
||||
if(removed == null) {
|
||||
removed = current;
|
||||
removedDecorations.put(object, removed);
|
||||
}//if//
|
||||
else {
|
||||
current.setNext(removed.getNext());
|
||||
removed.setNext(current);
|
||||
}//else//
|
||||
|
||||
current = next;
|
||||
}//if//
|
||||
else {
|
||||
previous = current;
|
||||
current = current.getNext();
|
||||
}//else//
|
||||
}//while//
|
||||
}//while//
|
||||
}//if//
|
||||
else {
|
||||
IIterator objectIterator = decorationMap.keyIterator();
|
||||
|
||||
while(objectIterator.hasNext()) {
|
||||
DecorationMetadata removed;
|
||||
|
||||
object = objectIterator.next();
|
||||
removed = (DecorationMetadata) decorationMap.remove(object);
|
||||
|
||||
if(removed != null) {
|
||||
DecorationMetadata next = removed;
|
||||
|
||||
//Locate the last decoration metadata in the linked list.//
|
||||
while(next.getNext() != null) {
|
||||
next = next.getNext();
|
||||
}//while//
|
||||
|
||||
next.setNext((DecorationMetadata) removedDecorations.get(object));
|
||||
removedDecorations.put(object, removed);
|
||||
}//if//
|
||||
}//while//
|
||||
}//else//
|
||||
}//else//
|
||||
}//clearDecorations()//
|
||||
/**
|
||||
* Adds a decoration to the given object and attached to the optional attribute.
|
||||
* @param object The object to be decorated.
|
||||
* @param attribute The optional attribute to be decorated.
|
||||
* @param decoration The decoration. Usually one of: ControlDecoration, HighlightDecoration, or ImageDecoration.
|
||||
*/
|
||||
public void addDecoration(Object object, Attribute attribute, AbstractDecoration decoration) {
|
||||
DecorationMetadata removedCurrent = (DecorationMetadata) removedDecorations.get(object);
|
||||
DecorationMetadata removedPrevious = null;
|
||||
DecorationMetadata newDecoration = new DecorationMetadata(object, attribute, decoration);
|
||||
|
||||
//Search the removed set for an identical decoration so we can simply move it back to the current set of decorations.//
|
||||
while(removedCurrent != null) {
|
||||
if(newDecoration.equals(removedCurrent)) {
|
||||
DecorationMetadata current = (DecorationMetadata) decorationMap.get(object);
|
||||
|
||||
//Extract the removed decoration metadata from the removed mapping.//
|
||||
if(removedPrevious == null) {
|
||||
removedDecorations.put(object, removedCurrent.getNext());
|
||||
}//if//
|
||||
else {
|
||||
removedPrevious.setNext(removedCurrent.getNext());
|
||||
}//else//
|
||||
|
||||
//Move the removed decoration metadata back into the current mapping.//
|
||||
if(current != null) {
|
||||
removedCurrent.setNext(current.getNext());
|
||||
current.setNext(removedCurrent);
|
||||
}//if//
|
||||
else {
|
||||
decorationMap.put(object, removedCurrent);
|
||||
removedCurrent.setNext(null);
|
||||
}//else//
|
||||
|
||||
//Exit the loop.//
|
||||
removedCurrent = null;
|
||||
newDecoration = null;
|
||||
}//if//
|
||||
else {
|
||||
removedCurrent = removedCurrent.getNext();
|
||||
}//else//
|
||||
}//while//
|
||||
|
||||
//If we really have a new decoration then place it in the added mapping.//
|
||||
if(newDecoration != null) {
|
||||
DecorationMetadata added = (DecorationMetadata) addedDecorations.get(object);
|
||||
|
||||
if(added != null) {
|
||||
newDecoration.setNext(added.getNext());
|
||||
added.setNext(newDecoration);
|
||||
}//if//
|
||||
else {
|
||||
addedDecorations.put(object, newDecoration);
|
||||
}//else//
|
||||
}//if//
|
||||
}//addDecoration()//
|
||||
}//DecorationManager//
|
||||
18
Foundation/src/com/foundation/controller/IController.java
Normal file
18
Foundation/src/com/foundation/controller/IController.java
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2007 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.controller;
|
||||
|
||||
import com.foundation.common.IEntity;
|
||||
|
||||
public interface IController extends IEntity {
|
||||
/**
|
||||
* Gets the decoration manager for this view.
|
||||
* @return The view's decoration manager.
|
||||
*/
|
||||
public DecorationManager getDecorationManager();
|
||||
}//IController//
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2007,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.controller;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.view.AbstractDecoration;
|
||||
|
||||
public interface IDecorationListener {
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param decoration The decoration added.
|
||||
*/
|
||||
public void addDecoration(AbstractDecoration decoration);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param decoration The decoration removed.
|
||||
*/
|
||||
public void removeDecoration(AbstractDecoration decoration);
|
||||
/**
|
||||
* Gets the object being decorated or whose attribute is decorated (whose markup will be listened to for changes if the attribute is null).
|
||||
* @return The object decorated.
|
||||
*/
|
||||
public Object getDecoratedObject();
|
||||
/**
|
||||
* Gets the object's decorated attribute (whose markup will be listened to for changes).
|
||||
* @return The attribute decorated, or null if the object will be decorated.
|
||||
*/
|
||||
public Attribute getDecoratedAttribute();
|
||||
}//IDecorationListener//
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 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.controller;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.view.AbstractDecoration;
|
||||
|
||||
public interface IDecorationMultiListener {
|
||||
/**
|
||||
* Determines whether the listener is interesting in listening to decorations attached to the given object and optional attribute.
|
||||
* @param decorationType The class of decoration.
|
||||
* @param object The object whose attribute (or which directly) has a decoration attached to it.
|
||||
* @param attribute The optional attribute on the given object, which if specified is decorated. If not specified then the object its self is decorated.
|
||||
* @return Whether the listener is interested in decorations attached to the given object/attribute.
|
||||
*/
|
||||
public boolean isListening(Class decorationType, Object object, Attribute attribute);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param decoration The decoration added.
|
||||
* @param object The object whose attribute (or which directly) has a decoration attached to it.
|
||||
* @param attribute The optional attribute on the given object, which if specified is decorated. If not specified then the object its self is decorated.
|
||||
*/
|
||||
public void addDecoration(AbstractDecoration decoration, Object object, Attribute attribute);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param decoration The decoration removed.
|
||||
* @param object The object whose attribute (or which directly) has a decoration attached to it.
|
||||
* @param attribute The optional attribute on the given object, which if specified is decorated. If not specified then the object its self is decorated.
|
||||
*/
|
||||
public void removeDecoration(AbstractDecoration decoration, Object object, Attribute attribute);
|
||||
}//IDecorationMultiListener//
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
/**
|
||||
* The proxiable interface for the model manager.
|
||||
*/
|
||||
public interface IModelManager {
|
||||
}//IModelManager//
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.util.IList;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.attribute.IReflectableCollection;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.ReadTransaction;
|
||||
|
||||
/**
|
||||
* Manages models on a single process and maintains a list of all models at all times.
|
||||
* This is the simplest of model managers and should be used for small collections of shared models.
|
||||
*/
|
||||
public interface ISingleListModelManager extends IModelManager {
|
||||
/**
|
||||
* Adds the entity to the shared set of models and to any attached repository.
|
||||
* Note that this can also be performed by modifying the collection returned by getEntities(Class).
|
||||
* @param entity The entity to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAddition Whether to return the added value.
|
||||
* @return The result of the add operation.
|
||||
*/
|
||||
public ReflectionResult addEntity(IEntity entity, boolean returnAddition);
|
||||
/**
|
||||
* Adds the entities to the shared set of models and to any attached repository.
|
||||
* Note that this can also be performed by modifying the collection returned by getEntities(Class).
|
||||
* @param entities The entities to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAdditions Whether to return the added values.
|
||||
* @return The result of the add operation.
|
||||
*/
|
||||
public ReflectionResult addEntities(IList entities, boolean returnAdditions);
|
||||
/**
|
||||
* Gets all entities of the given type.
|
||||
* @return The result object which, if of the type ReflectionSuccess, will reference a reflectable IManagedList that (once reflected) will contain reflections of all instances of the given type. This collection will be updated if any new instances are created within the system.
|
||||
*/
|
||||
public ReflectionResult getEntities();
|
||||
/**
|
||||
* Gets all the entities for the given set of keys.
|
||||
* @param keys The keys for the entities desired. These can be obtained by calling IEntity.entityGetKey().
|
||||
* @return The result object which, if of the type ReflectionSuccess, will reference a reflectable IManagedList that (once reflected) will contain reflections of all matching entities.
|
||||
*/
|
||||
public ReflectionResult findEntities(Object[] keys);
|
||||
/**
|
||||
* Gets all the entities matching the given query.
|
||||
* @param transaction The transaction that will be run to produce entities which will be used to find the shared entities. This is similar to getEntities(Object[] keys), except that the caller doesn't have to perform as much work (it will be performed in the manager). <b>This transaction MUST query for all key attributes in the entity (those marked in the class as AO_KEY when defined).</b>
|
||||
* @return The result object which, if of the type ReflectionSuccess, will reference a reflectable IManagedList that (once reflected) will contain reflections of all matching entities.
|
||||
*/
|
||||
public ReflectionResult findEntities(ReadTransaction transaction);
|
||||
/**
|
||||
* Removes (deletes) the entity from the shared set of models and from any attached repository.
|
||||
* @param entity The entity to be removed. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
*/
|
||||
public ReflectionResult removeEntity(IEntity entity);
|
||||
/**
|
||||
* Removes (deletes) the entities from the shared set of models and from any attached repository.
|
||||
* @param entities The collection of IReflectable instances to be removed. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
*/
|
||||
public ReflectionResult removeEntities(IList entities);
|
||||
}//ISingleListModelManager//
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.util.IList;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.attribute.IReflectableCollection;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.ReadTransaction;
|
||||
|
||||
/**
|
||||
* Manages models on a single process and maintains a map of models currently loaded into memory.
|
||||
* This should be used for large collections of shared models where a small number will be loaded at any given time.
|
||||
*/
|
||||
public interface ISingleMappedModelManager extends IModelManager {
|
||||
/**
|
||||
* Adds the entity to the shared set of models and to any attached repository.
|
||||
* @param entity The entity to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAddition Whether to return the added value.
|
||||
* @return The result of the add operation.
|
||||
*/
|
||||
public ReflectionResult addEntity(IEntity entity, boolean returnAddition);
|
||||
/**
|
||||
* Adds the entities to the shared set of models and to any attached repository.
|
||||
* @param entities The entities to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAdditions Whether to return the added values.
|
||||
* @return The result of the add operation.
|
||||
*/
|
||||
public ReflectionResult addEntities(IList entities, boolean returnAdditions);
|
||||
/**
|
||||
* Removes (deletes) the entity from the shared set of models and from any attached repository.
|
||||
* @param entity The entity to be removed. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
*/
|
||||
public ReflectionResult removeEntity(IEntity entity);
|
||||
/**
|
||||
* Removes (deletes) the entities from the shared set of models and from any attached repository.
|
||||
* @param entities The collection of IReflectable instances to be removed. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
*/
|
||||
public ReflectionResult removeEntities(IList entities);
|
||||
/**
|
||||
* Finds the first entity matching the query.
|
||||
* @param query The query by example object.
|
||||
* @return The IReflectable instance of the first result.
|
||||
*/
|
||||
public ReflectionResult findEntity(IEntity query);
|
||||
/**
|
||||
* Finds all entities matching the query.
|
||||
* @param query The query by example object.
|
||||
* @return The collection of IReflectable instances matching the query. Will never be null.
|
||||
*/
|
||||
public ReflectionResult findEntities(IEntity query);
|
||||
/**
|
||||
* Gets all the entities matching the given query.
|
||||
* @param transaction The transaction that will be run to produce entities which will be used to find the shared entities. This is similar to getEntities(Object[] keys), except that the caller doesn't have to perform as much work (it will be performed in the manager). <b>This transaction MUST query for all key attributes in the entity (those marked in the class as AO_KEY when defined).</b>
|
||||
* @return The IReflectable instance of the first result.
|
||||
*/
|
||||
public ReflectionResult findEntities(ReadTransaction transaction);
|
||||
/**
|
||||
* Gets all the entities for the given set of keys.
|
||||
* @param keys The keys for the entities desired. These can be obtained by calling IEntity.entityGetKey().
|
||||
* @return The IReflectable instance of the first result.
|
||||
*/
|
||||
public ReflectionResult findEntities(Object[] keys);
|
||||
}//ISingleMappedModelListener//
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2007,2008 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.controller;
|
||||
|
||||
public interface IVetoableRunnable {
|
||||
/**
|
||||
* Performs necessary actions with the result indicating whether the process that called the runnable should continue.
|
||||
* @return Whether the process should continue.
|
||||
*/
|
||||
public boolean run();
|
||||
}//IVetoableRunnable//
|
||||
640
Foundation/src/com/foundation/controller/ModelController.java
Normal file
640
Foundation/src/com/foundation/controller/ModelController.java
Normal file
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.controller;
|
||||
|
||||
import com.common.util.ICollection;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.exception.TransactionException;
|
||||
import com.foundation.transaction.TransactionContextHolder;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.transaction.Transaction;
|
||||
import com.foundation.util.HashSetManager;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.util.ListManager;
|
||||
|
||||
public class ModelController extends Controller {
|
||||
/**
|
||||
* A list manager that stores the models in the repository when they are added to the list and removes them from the repository when they are removed from the list.
|
||||
* <p>Warning: This manager can only handle entities that are attached to the same repository. If the entities in the collection come from multiple repositories this manager should not be used.</p>
|
||||
*/
|
||||
public static class ModelListManager extends ListManager {
|
||||
private boolean suspendRepositoryHandling = false;
|
||||
/**
|
||||
* Gets the flag indicating that all repository interaction should be suspended.
|
||||
* This is handy for initializing the collection, or for manipulating it manually.
|
||||
* @return Whether the suspend adding/removing collection values to/from the repository.
|
||||
*/
|
||||
public boolean suspendRepositoryHandling() {
|
||||
return suspendRepositoryHandling;
|
||||
}//suspendRepositoryHandling()//
|
||||
/**
|
||||
* Sets the flag indicating that all repository interaction should be suspended.
|
||||
* This is handy for initializing the collection, or for manipulating it manually.
|
||||
* @param suspendRepositoryHandling Whether the suspend adding/removing collection values to/from the repository.
|
||||
*/
|
||||
public void suspendRepositoryHandling(boolean suspendRepositoryHandling) {
|
||||
this.suspendRepositoryHandling = suspendRepositoryHandling;
|
||||
}//suspendRepositoryHandling()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnAdd()
|
||||
*/
|
||||
public boolean checkOnAdd() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnAdd()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnRemove()
|
||||
*/
|
||||
public boolean checkOnRemove() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnReplace()
|
||||
*/
|
||||
public boolean checkOnReplace() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnReplace()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAdd(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canAdd(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate();
|
||||
|
||||
if(info != null) {
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAdd()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, IManagedCollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.getSize() > 0) {
|
||||
boolean hasError = false;
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
|
||||
try {
|
||||
IIterator iterator = values.iterator();
|
||||
|
||||
//TODO: Would be nice to do a bulk add to the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
while(result && iterator.hasNext()) {
|
||||
Object value = iterator.next();
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.length > 0) {
|
||||
boolean hasError = false;
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
|
||||
try {
|
||||
//TODO: Would be nice to do a bulk add to the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
for(int valueIndex = values.length - 1; result && valueIndex >= 0; valueIndex--) {
|
||||
Object value = values[valueIndex];
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
if(!hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemove(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canRemove(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete();
|
||||
|
||||
if(info != null) {
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, byte context) {
|
||||
return canRemoveAll(managed, new LiteList(managed), context);
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, ICollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.getSize() > 0) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
IIterator iterator = values.iterator();
|
||||
|
||||
//TODO: Would be nice to do a bulk remove from the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
while(result && iterator.hasNext()) {
|
||||
Object value = iterator.next();
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//while//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.length > 0) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
//TODO: Would be nice to do a bulk remove from the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
for(int valueIndex = values.length - 1; result && valueIndex >= 0; valueIndex--) {
|
||||
Object value = values[valueIndex];
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canReplace(com.common.util.IManagedCollection, java.lang.Object, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canReplace(IManagedCollection managed, Object oldValue, Object newValue, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
if(oldValue instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(oldValue) {
|
||||
TransactionErrorInfo info = ((IEntity) oldValue).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
if(newValue instanceof IEntity && result) {
|
||||
TransactionErrorInfo info = ((IEntity) newValue).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canReplace()//
|
||||
}//ModelListManager//
|
||||
/**
|
||||
* A hash set manager that stores the models in the repository when they are added to the set and removes them from the repository when they are removed from the set.
|
||||
* <p>Warning: This manager can only handle entities that are attached to the same repository. If the entities in the collection come from multiple repositories this manager should not be used.</p>
|
||||
*/
|
||||
public static class ModelHashSetManager extends HashSetManager {
|
||||
private boolean suspendRepositoryHandling = false;
|
||||
/**
|
||||
* Gets the flag indicating that all repository interaction should be suspended.
|
||||
* This is handy for initializing the collection, or for manipulating it manually.
|
||||
* @return Whether the suspend adding/removing collection values to/from the repository.
|
||||
*/
|
||||
public boolean suspendRepositoryHandling() {
|
||||
return suspendRepositoryHandling;
|
||||
}//suspendRepositoryHandling()//
|
||||
/**
|
||||
* Sets the flag indicating that all repository interaction should be suspended.
|
||||
* This is handy for initializing the collection, or for manipulating it manually.
|
||||
* @param suspendRepositoryHandling Whether the suspend adding/removing collection values to/from the repository.
|
||||
*/
|
||||
public void suspendRepositoryHandling(boolean suspendRepositoryHandling) {
|
||||
this.suspendRepositoryHandling = suspendRepositoryHandling;
|
||||
}//suspendRepositoryHandling()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnAdd()
|
||||
*/
|
||||
public boolean checkOnAdd() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnAdd()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnRemove()
|
||||
*/
|
||||
public boolean checkOnRemove() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#checkOnReplace()
|
||||
*/
|
||||
public boolean checkOnReplace() {
|
||||
return !suspendRepositoryHandling;
|
||||
}//checkOnReplace()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAdd(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canAdd(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate();
|
||||
|
||||
if(info != null) {
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAdd()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, IManagedCollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.getSize() > 0) {
|
||||
boolean hasError = false;
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
|
||||
try {
|
||||
IIterator iterator = values.iterator();
|
||||
|
||||
//TODO: Would be nice to do a bulk add to the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
while(result && iterator.hasNext()) {
|
||||
Object value = iterator.next();
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.length > 0) {
|
||||
boolean hasError = false;
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
|
||||
try {
|
||||
//TODO: Would be nice to do a bulk add to the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
for(int valueIndex = values.length - 1; result && valueIndex >= 0; valueIndex--) {
|
||||
Object value = values[valueIndex];
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
if(!hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemove(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canRemove(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete();
|
||||
|
||||
if(info != null) {
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, byte context) {
|
||||
return canRemoveAll(managed, new LiteList(managed), context);
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, ICollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.getSize() > 0) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
IIterator iterator = values.iterator();
|
||||
|
||||
//TODO: Would be nice to do a bulk remove from the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
while(result && iterator.hasNext()) {
|
||||
Object value = iterator.next();
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//while//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED && values.length > 0) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
//TODO: Would be nice to do a bulk remove from the repository using a single query if possible. Not all objects may use the exact same query however!
|
||||
for(int valueIndex = values.length - 1; result && valueIndex >= 0; valueIndex--) {
|
||||
Object value = values[valueIndex];
|
||||
|
||||
if(value instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(value) {
|
||||
TransactionErrorInfo info = ((IEntity) value).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canReplace(com.common.util.IManagedCollection, java.lang.Object, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canReplace(IManagedCollection managed, Object oldValue, Object newValue, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context != CONTEXT_TRUSTED) {
|
||||
TransactionContextHolder transactionContext = TransactionContextHolder.getInstance(null);
|
||||
boolean hasError = false;
|
||||
|
||||
try {
|
||||
if(oldValue instanceof IEntity) {
|
||||
/*
|
||||
* Note: This forces the thread to discard its cached object data and causes it to get the latest data from main memory.
|
||||
* This does not ensure another thread will not access the model at the same time.
|
||||
* We don't care about preventing other threads from accessing the entity since we will only be accessing sudo-static data for the deletion operation.//
|
||||
*/
|
||||
synchronized(oldValue) {
|
||||
TransactionErrorInfo info = ((IEntity) oldValue).entityDelete(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
if(newValue instanceof IEntity && result) {
|
||||
TransactionErrorInfo info = ((IEntity) newValue).entityUpdate(transactionContext);
|
||||
|
||||
if(info != null) {
|
||||
hasError = true;
|
||||
managed.setErrorInfo(info);
|
||||
result = false;
|
||||
}//if//
|
||||
}//if//
|
||||
}//try//
|
||||
finally {
|
||||
if(hasError || !transactionContext.commit()) {
|
||||
transactionContext.rollback();
|
||||
}//if//
|
||||
|
||||
transactionContext.close();
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//canReplace()//
|
||||
}//ModelHashSetManager//
|
||||
/**
|
||||
* ModelController constructor.
|
||||
*/
|
||||
public ModelController() {
|
||||
}//ModelController()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IController#getDecorationManager()
|
||||
*/
|
||||
public DecorationManager getDecorationManager() {
|
||||
return null;
|
||||
}//getDecorationManager()//
|
||||
}//ModelController//
|
||||
477
Foundation/src/com/foundation/controller/ModelInterface.java
Normal file
477
Foundation/src/com/foundation/controller/ModelInterface.java
Normal file
@@ -0,0 +1,477 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.exception.MethodNotSupportedException;
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.common.orb.Orb;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.attribute.IReflectableCollection;
|
||||
import com.foundation.common.Entity;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.transaction.ReadTransaction;
|
||||
import com.foundation.util.IManagedList;
|
||||
|
||||
/**
|
||||
* The interface used by application components to access shared models regardless of where they are located.
|
||||
*
|
||||
* TODO: Reflect all results from a remote source. This should be "pass-through" in that any synchronizations automatically get forwarded to the reflected object and don't get applied if the reflected object rejects the change set.
|
||||
*
|
||||
* TODO: Listen to remote sources for lost connections - automatically reconnect the reflection(s) when a source is lost.
|
||||
*
|
||||
* TODO: Allow calls to some methods to not return the reflected object; Allow additional result information to be returned - such as the "duplicate value for Customer.LOGIN found" error.
|
||||
*
|
||||
*/
|
||||
public class ModelInterface {
|
||||
/** The one and only instance of this class. */
|
||||
private static final ModelInterface singleton = new ModelInterface();
|
||||
/**
|
||||
* ModelInterface constructor.
|
||||
*/
|
||||
private ModelInterface() {
|
||||
}//ModelInterface()//
|
||||
/**
|
||||
* Adds the entity to the shared set of models.
|
||||
* <p>This is used for Mapped and List type models.</p>
|
||||
* @param entity The entity to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAddition Whether to return the added value. This will be a local reflection if the model management is remote.
|
||||
* @return A result object containing the reflection of the added value if successful.
|
||||
*/
|
||||
public static ReflectionResult addEntity(IEntity entity, boolean returnAddition) {
|
||||
ReflectionResult result = null;
|
||||
Class type = entity != null ? entity.getClass() : null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(entity.isObjectRepositoryBound()) {
|
||||
if(!entity.isObjectNew()) {
|
||||
throw new IllegalArgumentException("Must only add new objects to the manager.");
|
||||
}//if//
|
||||
else if(entity.isReflection()) {
|
||||
throw new IllegalArgumentException("Cannot send a reflection.");
|
||||
}//else if//
|
||||
}//if//
|
||||
|
||||
if(modelManager != null) {
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
result = ((ISingleListModelManager) modelManager).addEntity(entity, returnAddition);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).addEntity(entity, returnAddition);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
|
||||
//Reflect the added value if it is returned and the management is remote.//
|
||||
if(returnAddition && !Orb.isLocal(modelManager) && result instanceof ReflectionSuccess && ((ReflectionSuccess) result).getResult() != null) {
|
||||
|
||||
//TODO: Reflect the result with a weak & pass through reflection manager.
|
||||
//TODO: Must ensure that synchronization to the reflection propegates automatically to the remote reflected object and any failures propegate back to the synchronizer.
|
||||
// reflect((IReflectable) ((ReflectionSuccess) result).getResult());
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//addEntity()//
|
||||
/**
|
||||
* Adds the entities to the shared set of models.
|
||||
* <p>This is used for Mapped and List type models.</p>
|
||||
* @param entities The list of entity instances to be added. This object must be serializable in the event that the model management occurs on a remote process.
|
||||
* @param returnAdditions
|
||||
* @return A result object containing an IList collection of reflectable references to the entities added. These may not be the same objects as was passed to this method.
|
||||
*/
|
||||
public static ReflectionResult addEntities(IList entities, boolean returnAdditions) {
|
||||
ReflectionResult result = null;
|
||||
Class type = entities == null || entities.getSize() == 0 || entities.getFirst() == null ? null : entities.getFirst().getClass();
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
for(int index = 0, size = entities.getSize(); index < size; index++) {
|
||||
Entity next = (Entity) entities.get(index);
|
||||
|
||||
if(!next.isObjectNew()) {
|
||||
throw new IllegalArgumentException("Must only add new objects to the manager.");
|
||||
}//if//
|
||||
else if(next.isReflection()) {
|
||||
throw new IllegalArgumentException("Cannot send a reflection.");
|
||||
}//else if//
|
||||
}//for//
|
||||
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
result = ((ISingleListModelManager) modelManager).addEntities(entities, returnAdditions);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).addEntities(entities, returnAdditions);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
|
||||
//If results were returned then reflect them using a pass through & weak reflection manager so that we have a single point to recover from failures.//
|
||||
if(returnAdditions && !Orb.isLocal(modelManager) && result instanceof ReflectionSuccess && ((ReflectionSuccess) result).getResult() instanceof IList && ((IList) ((ReflectionSuccess) result).getResult()).getSize() > 0) {
|
||||
//Reflect the results.//
|
||||
// reflectAll((IList) ((ReflectionSuccess) result).getResult());
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(LiteList.EMPTY_LIST);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//addEntity()//
|
||||
/**
|
||||
* Removes (deletes) the entity from the shared set of models. The values will also be deleted to any attached repository.
|
||||
* <p>This is used for Mapped and List type models.</p>
|
||||
* @param entity The entity to be removed. This object must be serializable in the event that the model management occurs on a remote process. The value may be a reflection or proxy - the code will de-reflect and de-proxy where possible.
|
||||
* @return A result object reporting success or failure of the operation.
|
||||
*/
|
||||
public static ReflectionResult removeEntity(IEntity entity) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
try {
|
||||
if(entity != null) {
|
||||
Class type = Orb.isProxy(entity) ? Orb.getNonProxyClass(entity) : entity.getClass();
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
//First ensure we are not using a local proxy.//
|
||||
if(Orb.isProxy(entity) && Orb.isLocal(entity)) {
|
||||
entity = (IEntity) Orb.getLocal(entity);
|
||||
}//if//
|
||||
|
||||
//Ensure we don't send a reflection (it can't be a reflection if it is a proxy).//
|
||||
while(!Orb.isProxy(entity) && entity.isReflection()) {
|
||||
entity = (IEntity) entity.getReflected();
|
||||
|
||||
//De-proxy the object if possible.//
|
||||
if(Orb.isProxy(entity) && Orb.isLocal(entity)) {
|
||||
entity = (IEntity) Orb.getLocal(entity);
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
((ISingleListModelManager) modelManager).removeEntity(entity);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
((ISingleMappedModelManager) modelManager).removeEntity(entity);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
|
||||
result = new ReflectionSuccess(null);
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//Shouldn't ever get here.//
|
||||
throw new ClassNotFoundException();
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new NullPointerException();
|
||||
}//else//
|
||||
}//try//
|
||||
catch(ClassNotFoundException e) {
|
||||
Debug.log(e);
|
||||
result = new ReflectionCustomError(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//removeEntity()//
|
||||
/**
|
||||
* Removes (deletes) the entities from the shared set of models. The values will also be deleted to any attached repository.
|
||||
* <p>This is used for Mapped and List type models.</p>
|
||||
* @param entities The list of entity instances to be removed. This object must be serializable in the event that the model management occurs on a remote process. List values may be reflections or proxies - the code will de-reflect and de-proxy where possible.
|
||||
* @return A result object reporting success or failure of the operation.
|
||||
*/
|
||||
public static ReflectionResult removeEntities(IList entities) {
|
||||
ReflectionResult result;
|
||||
|
||||
try {
|
||||
IReflectable first = entities == null || entities.getSize() == 0 || entities.getFirst() == null ? null : (IReflectable) entities.getFirst();
|
||||
Class type = Orb.isProxy(first) ? Orb.getNonProxyClass(first) : first.getClass();
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
//Iterate over the entities and de-reflect and de-proxy where ever possible.//
|
||||
for(int index = 0, size = entities.getSize(); index < size; index++) {
|
||||
IReflectable next = (IReflectable) entities.get(index);
|
||||
|
||||
//First ensure we are not using a local proxy.//
|
||||
if(Orb.isProxy(next) && Orb.isLocal(next)) {
|
||||
next = (IReflectable) Orb.getLocal(next);
|
||||
}//if//
|
||||
|
||||
//Ensure we don't send a reflection (it can't be a reflection if it is a proxy).//
|
||||
while(!Orb.isProxy(next) && next.isReflection()) {
|
||||
next = next.getReflected();
|
||||
|
||||
//De-proxy the object if possible.//
|
||||
if(Orb.isProxy(next) && Orb.isLocal(next)) {
|
||||
next = (IReflectable) Orb.getLocal(next);
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
entities.set(index, next);
|
||||
}//for//
|
||||
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
((ISingleListModelManager) modelManager).removeEntities(entities);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
((ISingleMappedModelManager) modelManager).removeEntities(entities);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
|
||||
result = new ReflectionSuccess(null);
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//Shouldn't ever get here.//
|
||||
throw new ClassNotFoundException();
|
||||
}//else//
|
||||
}//try//
|
||||
catch(ClassNotFoundException e) {
|
||||
Debug.log(e);
|
||||
result = new ReflectionCustomError(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//removeEntities()//
|
||||
/**
|
||||
* Finds the first entity matching the query.
|
||||
* @param query The query by example object.
|
||||
* @return A result object containing an IReflectable instance of the first result if the operation was successful.
|
||||
*/
|
||||
public static ReflectionResult findEntity(IEntity query) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
try {
|
||||
Class type = query != null ? query.getClass() : null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
if(query.isReflection()) {
|
||||
throw new IllegalArgumentException("Cannot send a reflection.");
|
||||
}//if//
|
||||
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
throw new MethodNotSupportedException("The class " + type.getName() + " uses a list model manager which doesn't support querying for elements.");
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).findEntity(query);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
}//try//
|
||||
// catch(ClassNotFoundException e) {
|
||||
// Debug.log(e);
|
||||
// result = new ReflectionCustomError(e);
|
||||
// }//catch//
|
||||
finally {}
|
||||
|
||||
return result;
|
||||
}//findEntity()//
|
||||
/**
|
||||
* Finds all entities matching the query.
|
||||
* @param query The query by example object.
|
||||
* @return A result object containing an IList containing IReflectable instances matching the query if the operation was successful.
|
||||
*/
|
||||
public static ReflectionResult findEntities(IEntity query) {
|
||||
ReflectionResult result = null;
|
||||
Class type = query != null ? query.getClass() : null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
if(query.isReflection()) {
|
||||
throw new IllegalArgumentException("Cannot send a reflection.");
|
||||
}//if//
|
||||
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
throw new MethodNotSupportedException("The class " + type.getName() + " uses a list model manager which doesn't support querying for elements.");
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).findEntities(query);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//findEntity()//
|
||||
/**
|
||||
* Gets all entities of the given type.
|
||||
* @param type The model type whose instances will be returned.
|
||||
* @return A result object containing an IReflectableCollection (reflect this which will contain reflections of all instances of the given type) if the operation was successful.
|
||||
*/
|
||||
public static ReflectionResult getEntities(Class type) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
result = ((ISingleListModelManager) modelManager).getEntities();
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
throw new MethodNotSupportedException("The class " + type.getName() + " uses a mapped model manager which doesn't support retrieving a collection of all elements.");
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//getEntities()//
|
||||
/**
|
||||
* Gets all (or first) entities of the given type.
|
||||
* @param type The model type whose instances will be returned.
|
||||
* @param transaction The read many or read single transaction to be run. The transaction must result in instance(s) of the passed <code>type</code>.
|
||||
* @return A result object containing an IReflectableCollection (reflect this which will contain reflections of all instances of the given type) if the operation was successful.
|
||||
*/
|
||||
public static ReflectionResult findEntities(Class type, ReadTransaction transaction) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
result = ((ISingleListModelManager) modelManager).findEntities(transaction);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).findEntities(transaction);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//findEntities()//
|
||||
/**
|
||||
* Gets all entities of the given type.
|
||||
* @param type The model type whose instances will be returned.
|
||||
* @param keys The set of keys that uniquely identify each model to be found.
|
||||
* @return A result object containing an IReflectableCollection (reflect this which will contain reflections of all instances of the given type) if the operation was successful.
|
||||
*/
|
||||
public static ReflectionResult findEntities(Class type, Object[] keys) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
if(type != null) {
|
||||
IModelManager modelManager = singleton.locateModelManager(type);
|
||||
|
||||
if(modelManager != null) {
|
||||
if(modelManager instanceof ISingleListModelManager) {
|
||||
result = ((ISingleListModelManager) modelManager).findEntities(keys);
|
||||
}//if//
|
||||
else if(modelManager instanceof ISingleMappedModelManager) {
|
||||
result = ((ISingleMappedModelManager) modelManager).findEntities(keys);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid model manager type.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Specify an identifier.
|
||||
result = new ReflectionNetworkError(0, "Failed to locate a Model Manager.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//findEntities()//
|
||||
/**
|
||||
* Locates the model manager
|
||||
* @param type The class whose model manager is to be found.
|
||||
* @return The non-null model manager reference.
|
||||
*/
|
||||
private IModelManager locateModelManager(Class type) {
|
||||
IModelManager result = null;
|
||||
|
||||
//The model manager will always be local. Each process has its own model manager which synchronizes with others via the ADA server network (when the app is deployed).//
|
||||
result = ModelManagerContainer.getSingleton().getModelManager(type);
|
||||
|
||||
if(result == null) {
|
||||
throw new IllegalArgumentException("The class " + type.getName() + " either is not marked for management or a model manager could not be found. The type should contain something similar to this code: static {registerTypeMetadata(xxxx.class, Entity.AO_MANAGEMENT_TYPE_LOAD_AS_NEEDED);}");
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//locateModelManager()//
|
||||
}//ModelInterface//
|
||||
57
Foundation/src/com/foundation/controller/ModelManager.java
Normal file
57
Foundation/src/com/foundation/controller/ModelManager.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.foundation.application.IApplication;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.TransactionService;
|
||||
|
||||
/**
|
||||
* The automated model controller that is capable of all common model controller functionality.
|
||||
*/
|
||||
public abstract class ModelManager extends ModelController implements IModelManager {
|
||||
/** The type of model supported by this model manager. */
|
||||
private Class type;
|
||||
/** Whether to keep the models in sync with other managers in other processes. */
|
||||
private boolean sync;
|
||||
/** The application servicing this model. */
|
||||
private IApplication application;
|
||||
/**
|
||||
* ModelManager constructor.
|
||||
*/
|
||||
protected ModelManager(Class type, boolean sync) {
|
||||
this.type = type;
|
||||
this.sync = sync;
|
||||
|
||||
try {
|
||||
Object instance = type.newInstance();
|
||||
|
||||
application = ((IEntity) instance).getApplication();
|
||||
}//try//
|
||||
catch(InstantiationException e) {
|
||||
Debug.log("Caught exception while initializing an empty instance for framework use.", e);
|
||||
}//catch//
|
||||
catch(IllegalAccessException e) {
|
||||
Debug.log("All models must provide a public default constructor for framework initialization of an empty instance.", e);
|
||||
}//catch//
|
||||
}//ModelManager()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Entity#getApplication()
|
||||
*/
|
||||
public IApplication getApplication() {
|
||||
return application;
|
||||
}//getApplication()//
|
||||
/**
|
||||
* Gets the class of model managed.
|
||||
* @return The model type being managed.
|
||||
*/
|
||||
public Class getType() {
|
||||
return type;
|
||||
}//getType()//
|
||||
/**
|
||||
* Gets whether to keep the models in sync with other managers in other processes.
|
||||
* @return If the models must be sync'd with other processes.
|
||||
*/
|
||||
public boolean sync() {
|
||||
return sync;
|
||||
}//sync()//
|
||||
}//ModelManager//
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
import com.foundation.metadata.TypeMetadata;
|
||||
|
||||
public class ModelManagerContainer {
|
||||
/** The mapping of class to model manager instance hosted on this process. */
|
||||
private LiteHashMap typeToModelManagerMap = new LiteHashMap(40);
|
||||
/** The one and only instance of this class. */
|
||||
private static final ModelManagerContainer singleton = new ModelManagerContainer();
|
||||
/**
|
||||
* ModelManagerContainer constructor.
|
||||
*/
|
||||
private ModelManagerContainer() {
|
||||
}//ModelManagerContainer()//
|
||||
/**
|
||||
* Gets the only instance of this class.
|
||||
* @return The singleton instance.
|
||||
*/
|
||||
public static ModelManagerContainer getSingleton() {
|
||||
return singleton;
|
||||
}//getSingleton()//
|
||||
/**
|
||||
* Gets the local model manager.
|
||||
* @param type The type of model the manager supports.
|
||||
* @return The model manager.
|
||||
*/
|
||||
public synchronized ModelManager getModelManager(Class type) {
|
||||
ModelManager result = (ModelManager) typeToModelManagerMap.get(type);
|
||||
|
||||
if(result == null) {
|
||||
TypeMetadata metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
|
||||
switch(metadata.getManagementType()) {
|
||||
default:
|
||||
case TypeMetadata.MANAGEMENT_TYPE_NONE:
|
||||
break;
|
||||
case TypeMetadata.MANAGEMENT_TYPE_LOAD_ALL: {
|
||||
result = new SingleListModelManager(type, true);
|
||||
typeToModelManagerMap.put(type, result);
|
||||
break;
|
||||
}//case//
|
||||
case TypeMetadata.MANAGEMENT_TYPE_LOAD_AS_NEEDED: {
|
||||
result = new SingleMappedModelManager(type, true);
|
||||
typeToModelManagerMap.put(type, result);
|
||||
break;
|
||||
}//case//
|
||||
case TypeMetadata.MANAGEMENT_TYPE_LOAD_ALL_LOCAL_ONLY: {
|
||||
result = new SingleListModelManager(type, false);
|
||||
typeToModelManagerMap.put(type, result);
|
||||
break;
|
||||
}//case//
|
||||
case TypeMetadata.MANAGEMENT_TYPE_LOAD_AS_NEEDED_LOCAL_ONLY: {
|
||||
result = new SingleMappedModelManager(type, true);
|
||||
typeToModelManagerMap.put(type, result);
|
||||
break;
|
||||
}//case//
|
||||
}//switch//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getModelManager()//
|
||||
}//ModelManagerContainer//
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a failure of the model to match the constraint requirements.
|
||||
*/
|
||||
public class ReflectionConstraintError extends ReflectionResult {
|
||||
/** The repository or application defined constraint identifier. */
|
||||
private String constraint = null;
|
||||
/**
|
||||
* ReflectionConstraintError constructor.
|
||||
*/
|
||||
public ReflectionConstraintError() {
|
||||
}//ReflectionConstraintError()//
|
||||
/**
|
||||
* ReflectionConstraintError constructor.
|
||||
* @param constraint The repository or application defined constraint identifier.
|
||||
*/
|
||||
public ReflectionConstraintError(String constraint) {
|
||||
this.constraint = constraint;
|
||||
}//ReflectionConstraintError()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_CONSTRAINT_ERROR;
|
||||
}//getResultType()//
|
||||
/**
|
||||
* Gets the constraint identifier useable by the application to re-attempt the synchronization or warn the user.
|
||||
* @return The repository or application defined constraint identifier.
|
||||
*/
|
||||
public String getConstraint() {
|
||||
return constraint;
|
||||
}//getConstraint()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
constraint = in.readUTF8();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeUTF8(constraint);
|
||||
}//writeExternal()//
|
||||
}//ReflectionConstraintError//
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a problem that could not be expressed with one of the other error result types.
|
||||
*/
|
||||
public class ReflectionCustomError extends ReflectionResult {
|
||||
/** The custom error object. */
|
||||
private Object errorValue = null;
|
||||
/**
|
||||
* ReflectionCustomError constructor.
|
||||
*/
|
||||
public ReflectionCustomError() {
|
||||
}//ReflectionCustomError()//
|
||||
/**
|
||||
* ReflectionCustomError constructor.
|
||||
* @param errorValue The custom error object.
|
||||
*/
|
||||
public ReflectionCustomError(Object errorValue) {
|
||||
this.errorValue = errorValue;
|
||||
}//ReflectionCustomError()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return 0;
|
||||
}//getResultType()//
|
||||
/**
|
||||
* Gets the error value assigned to the custom error.
|
||||
* @return The custom error object.
|
||||
*/
|
||||
public Object getErrorValue() {
|
||||
return errorValue;
|
||||
}//getErrorValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
errorValue = in.readObject();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeObject(errorValue);
|
||||
}//writeExternal()//
|
||||
}//ReflectionCustomError//
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a failure in the Brainstorm Framework.
|
||||
*/
|
||||
public class ReflectionFrameworkError extends ReflectionResult {
|
||||
/** An identifier for the error. */
|
||||
private int errorId = 0;
|
||||
/** Some optional text for the error. */
|
||||
private String errorText = null;
|
||||
/**
|
||||
* ReflectionFrameworkError constructor.
|
||||
*/
|
||||
public ReflectionFrameworkError() {
|
||||
}//ReflectionFrameworkError()//
|
||||
/**
|
||||
* ReflectionFrameworkError constructor.
|
||||
* @param errorId The identifier for the error.
|
||||
* @param errorText The text associated with the error.
|
||||
*/
|
||||
public ReflectionFrameworkError(int errorId, String errorText) {
|
||||
this.errorId = errorId;
|
||||
this.errorText = errorText;
|
||||
}//ReflectionFrameworkError()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_FRAMEWORK_ERROR;
|
||||
}//getResultType()//
|
||||
/**
|
||||
* Gets the error identifier.
|
||||
* @return An identifier for the error.
|
||||
*/
|
||||
public int getErrorId() {
|
||||
return errorId;
|
||||
}//getException()//
|
||||
/**
|
||||
* Gets the optional error text.
|
||||
* @return Some optional text for the error.
|
||||
*/
|
||||
public String getErrorText() {
|
||||
return errorText;
|
||||
}//getErrorText()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
errorId = in.readInt();
|
||||
errorText = in.readUTF8();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(errorId);
|
||||
out.writeUTF8(errorText);
|
||||
}//writeExternal()//
|
||||
}//ReflectionFrameworkError//
|
||||
174
Foundation/src/com/foundation/controller/ReflectionManager.java
Normal file
174
Foundation/src/com/foundation/controller/ReflectionManager.java
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import com.common.util.ICollection;
|
||||
import com.common.util.IList;
|
||||
import com.foundation.attribute.SynchronizeFailureCause;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.attribute.ReflectionContext;
|
||||
import com.foundation.attribute.ReflectionContext.IValidationHandler;
|
||||
|
||||
public class ReflectionManager extends Object implements IValidationHandler {
|
||||
/** The view controller associated with this reflection manager. All modifications to the reflections must occur via the view controller's event thread. */
|
||||
private AbstractViewController viewController = null;
|
||||
/** The reflection context (which may be shared) used to control the reflections. */
|
||||
private ReflectionContext reflectionContext = null;
|
||||
/**
|
||||
* ReflectionManager constructor.
|
||||
* <p>Note: This class will initialize the reflection context. The creator <b>must</b> release this instance when it is no longer needed.</p>
|
||||
* @param viewController The view controller associated with this reflection manager.
|
||||
* @param reflectionContext The reflection context used by the manager to create and control reflections.
|
||||
* @param updateRepository Whether the repository should be updated when reflections created by this reflection context are synchronized. By default this is true.
|
||||
*/
|
||||
public ReflectionManager(AbstractViewController viewController, ReflectionContext reflectionContext) {
|
||||
super();
|
||||
|
||||
if(viewController == null) {
|
||||
throw new IllegalArgumentException("Must provide a valid view controller when creating a reflection manager.");
|
||||
}//if//
|
||||
|
||||
if(reflectionContext == null) {
|
||||
throw new IllegalArgumentException("Must provide a valid reflection context when creating a reflection manager.");
|
||||
}//if//
|
||||
|
||||
this.viewController = viewController;
|
||||
this.reflectionContext = reflectionContext;
|
||||
reflectionContext.initialize();
|
||||
}//ReflectionManager()//
|
||||
/**
|
||||
* Gets the reflection context that is controlling the reflections for this reflection manager.
|
||||
* @return The underlying reflection context.
|
||||
*/
|
||||
public ReflectionContext getReflectionContext() {
|
||||
return reflectionContext;
|
||||
}//getReflectionContext()//
|
||||
/**
|
||||
* Releases this reflection manager when it is no longer required.
|
||||
* <p>This method will notify the underlying reflection context that it is no longer being used and any registered reflections may be unregistered.</p>
|
||||
*/
|
||||
public void release() {
|
||||
if(reflectionContext != null) {
|
||||
reflectionContext.release();
|
||||
reflectionContext = null;
|
||||
viewController = null;
|
||||
}//if//
|
||||
}//release()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.ReflectionContext.IValidationHandler#validate()
|
||||
*/
|
||||
public boolean validate() {
|
||||
return viewController.validate();
|
||||
}//validate()//
|
||||
/**
|
||||
* Creates a reflection of the given reflectable object. If the reflection already exists in this context or the value is a reflection in this context, then the existing reflection will be returned.
|
||||
* <p><b>Warning: As with all Entities (or subclasses thereof), the reflectable object should have a public default constructor which does not throw exceptions.</b></p>
|
||||
* @param value The value to be reflected.
|
||||
* @return The reflection of the value or the value if it is a reflection in this context. This will only be null if there was a problem constructing a new instance of the reflectable value.
|
||||
*/
|
||||
public IReflectable createReflection(IReflectable value) {
|
||||
return reflectionContext.createReflection(value);
|
||||
}//createReflection()//
|
||||
/**
|
||||
* Creates reflections of the given reflectable objects. If some or all of the reflections already exist in this context or some of the values are reflections in this context, then the existing reflections will be returned.
|
||||
* <p><b>Warning: As with all Entities (or subclasses thereof), the reflectable objects should have a public default constructor which does not throw exceptions.</b></p>
|
||||
* @param values The reflectable objects whose reflections will be retrieved.
|
||||
* @return The collection of reflections.
|
||||
*/
|
||||
public IList createReflections(IList values) {
|
||||
return reflectionContext.createReflections(values);
|
||||
}//createReflections()//
|
||||
/**
|
||||
* Synchronizes changes to a reflection with the object it reflects. In the case of a collection, the added and/or removed objects will be applied to the reflected collection.
|
||||
* @param reflection The reflection to synchronize.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeSingle(IReflectable reflection) {
|
||||
return synchronizeSingle(reflection, null, true);
|
||||
}//synchronizeSingle()//
|
||||
/**
|
||||
* Synchronizes changes to a reflection with the object it reflects. In the case of a collection, the added and/or removed objects will be applied to the reflected collection.
|
||||
* @param reflection The reflection to synchronize.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeSingle(IReflectable reflection, IValidationHandler validationHandler) {
|
||||
return synchronizeSingle(reflection, validationHandler, true);
|
||||
}//synchronizeSingle()//
|
||||
/**
|
||||
* Synchronizes changes to a reflection with the object it reflects. In the case of a collection, the added and/or removed objects will be applied to the reflected collection.
|
||||
* @param reflection The reflection to synchronize.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @param autoRetry Whether the synchronization should repeatedly retry until successful. If this is true then a failure will cause a re-call to the validation code before re-attempting synchronization.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeSingle(IReflectable reflection, IValidationHandler validationHandler, boolean autoRetry) {
|
||||
return reflectionContext.synchronizeSingle(reflection, validationHandler == null ? this : validationHandler, autoRetry);
|
||||
}//synchronizeSingle()//
|
||||
/**
|
||||
* Synchronizes changes to all given reflections with the objects they reflect.
|
||||
* <p>Warning: This will not synchronize the collection if the passed collection is a reflection!</p>
|
||||
* @param reflections The collection of reflections to be synchronized.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeMany(ICollection reflections) {
|
||||
return synchronizeMany(reflections, null, true);
|
||||
}//synchronizeMany()//
|
||||
/**
|
||||
* Synchronizes changes to all given reflections with the objects they reflect.
|
||||
* <p>Warning: This will not synchronize the collection if the passed collection is a reflection!</p>
|
||||
* @param reflections The collection of reflections to be synchronized.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeMany(ICollection reflections, IValidationHandler validationHandler) {
|
||||
return synchronizeMany(reflections, validationHandler, true);
|
||||
}//synchronizeMany()//
|
||||
/**
|
||||
* Synchronizes changes to all given reflections with the objects they reflect.
|
||||
* <p>Warning: This will not synchronize the collection if the passed collection is a reflection!</p>
|
||||
* @param reflections The collection of reflections to be synchronized.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @param autoRetry Whether the synchronization should repeatedly retry until successful. If this is true then a failure will cause a re-call to the validation code before re-attempting synchronization.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeMany(ICollection reflections, IValidationHandler validationHandler, boolean autoRetry) {
|
||||
return reflectionContext.synchronizeMany(reflections, validationHandler == null ? this : validationHandler, autoRetry);
|
||||
}//synchronizeMany()//
|
||||
/**
|
||||
* Gets the extended error information for the synchronization operation.
|
||||
* @return The cause for the failure of the last run synchronization operation. This will be null if there wasn't a failure.
|
||||
*/
|
||||
public SynchronizeFailureCause getSynchronizeFailureCause() {
|
||||
return getReflectionContext().getSynchronizationFailureCause();
|
||||
}//getSynchronizationFailureCause()//
|
||||
/**
|
||||
* Synchronizes changes to all reflections with the objects they reflect.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeAll() {
|
||||
return synchronizeAll(null, true);
|
||||
}//synchronizeAll()//
|
||||
/**
|
||||
* Synchronizes changes to all reflections with the objects they reflect.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeAll(IValidationHandler validationHandler) {
|
||||
return synchronizeAll(validationHandler, true);
|
||||
}//synchronizeAll()//
|
||||
/**
|
||||
* Synchronizes changes to all reflections with the objects they reflect.
|
||||
* @param validationHandler An optional validation handler which will be called prior to synchronizing. If null then the default validation will occur.
|
||||
* @param autoRetry Whether the synchronization should repeatedly retry until successful. If this is true then a failure will cause a re-call to the validation code before re-attempting synchronization.
|
||||
* @return Whether the synchronization was completely successful. Note that it could be partially successful since failures occur along virtual model lines.
|
||||
*/
|
||||
public boolean synchronizeAll(IValidationHandler validationHandler, boolean autoRetry) {
|
||||
return reflectionContext.synchronizeAll(validationHandler == null ? this : validationHandler, autoRetry);
|
||||
}//synchronizeAll()//
|
||||
}//ReflectionManager//
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a problem in the model preparation or versioning system.
|
||||
*/
|
||||
public class ReflectionModelError extends ReflectionResult {
|
||||
/** The model error code identifier (IEntity.MODEL_ERROR_xxx). */
|
||||
private int modelErrorCode = IEntity.MODEL_ERROR_NONE;
|
||||
/**
|
||||
* ReflectionModelError constructor.
|
||||
*/
|
||||
public ReflectionModelError() {
|
||||
}//ReflectionModelError()//
|
||||
/**
|
||||
* ReflectionModelError constructor.
|
||||
* @param modelErrorCode The model error code identifier (IEntity.MODEL_ERROR_xxx).
|
||||
*/
|
||||
public ReflectionModelError(int modelErrorCode) {
|
||||
this.modelErrorCode = modelErrorCode;
|
||||
}//ReflectionModelError()//
|
||||
/**
|
||||
* Gets the model synchronization error code detailing the problem.
|
||||
* @return The model error code identifier (IEntity.MODEL_ERROR_xxx).
|
||||
*/
|
||||
public int getModelErrorCode() {
|
||||
return modelErrorCode;
|
||||
}//getModelErrorCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_MODEL_ERROR;
|
||||
}//getResultType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
modelErrorCode = in.readInt();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(modelErrorCode);
|
||||
}//writeExternal()//
|
||||
}//ReflectionModelError//
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a failure in the network.
|
||||
*/
|
||||
public class ReflectionNetworkError extends ReflectionResult {
|
||||
/** An identifier for the error. */
|
||||
private int errorId = 0;
|
||||
/** Some optional text for the error. */
|
||||
private String errorText = null;
|
||||
/**
|
||||
* ReflectionNetworkError constructor.
|
||||
*/
|
||||
public ReflectionNetworkError() {
|
||||
}//ReflectionNetworkError()//
|
||||
/**
|
||||
* ReflectionNetworkError constructor.
|
||||
* @param errorId The identifier for the error.
|
||||
* @param errorText The text associated with the error.
|
||||
*/
|
||||
public ReflectionNetworkError(int errorId, String errorText) {
|
||||
this.errorId = errorId;
|
||||
this.errorText = errorText;
|
||||
}//ReflectionNetworkError()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_FRAMEWORK_ERROR;
|
||||
}//getResultType()//
|
||||
/**
|
||||
* Gets the error identifier.
|
||||
* @return An identifier for the error.
|
||||
*/
|
||||
public int getErrorId() {
|
||||
return errorId;
|
||||
}//getException()//
|
||||
/**
|
||||
* Gets the optional error text.
|
||||
* @return Some optional text for the error.
|
||||
*/
|
||||
public String getErrorText() {
|
||||
return errorText;
|
||||
}//getErrorText()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
errorId = in.readInt();
|
||||
errorText = in.readUTF8();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(errorId);
|
||||
out.writeUTF8(errorText);
|
||||
}//writeExternal()//
|
||||
}//ReflectionNetworkError//
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.io.IExternalizable;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.Transaction;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
|
||||
/**
|
||||
* A reflection operation result.
|
||||
*/
|
||||
public abstract class ReflectionResult implements IExternalizable {
|
||||
public static final int RESULT_TYPE_SUCCESS = 0;
|
||||
public static final int RESULT_TYPE_CONSTRAINT_ERROR = 1;
|
||||
public static final int RESULT_TYPE_TRANSACTIONAL_ERROR = 2;
|
||||
public static final int RESULT_TYPE_MODEL_ERROR = 3;
|
||||
public static final int RESULT_TYPE_NETWORK_ERROR = 4;
|
||||
public static final int RESULT_TYPE_FRAMEWORK_ERROR = 5;
|
||||
/**
|
||||
* Creates a result object from a given error object.
|
||||
* @param object An object that identifies the error.
|
||||
* @return The reflection result error object wrappering the passed object (will be the object if it is an instance of ReflectionResult).
|
||||
*/
|
||||
public static ReflectionResult createResult(Object object) {
|
||||
if(object instanceof ReflectionResult) {
|
||||
return (ReflectionResult) object;
|
||||
}//if//
|
||||
else if(object instanceof TransactionErrorInfo) {
|
||||
TransactionErrorInfo info = (TransactionErrorInfo) object;
|
||||
|
||||
switch(info.getTransactionErrorCode()) {
|
||||
default:
|
||||
case Transaction.ERROR_NONE: {
|
||||
switch(info.getModelErrorCode()) {
|
||||
case IEntity.MODEL_ERROR_UNKOWN_ERROR:
|
||||
case IEntity.MODEL_ERROR_BAD_VERSION:
|
||||
case IEntity.MODEL_ERROR_FAILED_PREPERATIONS: {
|
||||
return new ReflectionModelError(info.getModelErrorCode());
|
||||
}//case()//
|
||||
default:
|
||||
case IEntity.MODEL_ERROR_NONE: {
|
||||
return new ReflectionCustomError(info.getErrorData());
|
||||
}//case()//
|
||||
}//switch//
|
||||
}//case//
|
||||
case Transaction.ERROR_UNEXPECTED:
|
||||
case Transaction.ERROR_QUERY_GENERATION:
|
||||
case Transaction.ERROR_INCOMPATABLE_REPOSITORY:
|
||||
case Transaction.ERROR_COMMIT_FAILED:
|
||||
case Transaction.ERROR_RESOURCE_FAILURE:
|
||||
case Transaction.ERROR_RESOURCE_TIMEOUT: {
|
||||
return new ReflectionTransactionError(info.getTransactionErrorCode());
|
||||
}//case//
|
||||
case Transaction.ERROR_CONSTRAINT_VIOLATED: {
|
||||
return new ReflectionConstraintError(info.getErrorData() != null ? info.getErrorData().toString() : null);
|
||||
}//case//
|
||||
}//switch//
|
||||
}//else if//
|
||||
else {
|
||||
return new ReflectionCustomError(object);
|
||||
}//else//
|
||||
}//createResult()//
|
||||
/**
|
||||
* Gets the type code for the result.
|
||||
* @return One of the RESULT_TYPE_xxx identifiers.
|
||||
*/
|
||||
public abstract int getResultType();
|
||||
}//ReflectionResult//
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating the success of the operation and providing any available result.
|
||||
*/
|
||||
public class ReflectionSuccess extends ReflectionResult {
|
||||
/** The optional result object reference. */
|
||||
private Object result = null;
|
||||
/**
|
||||
* ReflectionSuccess constructor.
|
||||
* @param result The result of the synchronization, if there is one.
|
||||
*/
|
||||
public ReflectionSuccess() {
|
||||
}//ReflectionSuccess()//
|
||||
/**
|
||||
* ReflectionSuccess constructor.
|
||||
* @param result The result of the synchronization, if there is one.
|
||||
*/
|
||||
public ReflectionSuccess(Object result) {
|
||||
this.result = result;
|
||||
}//ReflectionSuccess()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_SUCCESS;
|
||||
}//getResultType()//
|
||||
/**
|
||||
* Gets the result of the operation.
|
||||
* @return The optional result object reference.
|
||||
*/
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}//getResult()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
result = in.readObject();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeObject(result);
|
||||
}//writeExternal()//
|
||||
}//ReflectionSuccess//
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.foundation.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* A reflection operation result indicating a problem in the transactional system (not including detected constraint errors).
|
||||
*/
|
||||
public class ReflectionTransactionError extends ReflectionResult {
|
||||
/** The transaction error code identifier (Transaction.ERROR_xxx). */
|
||||
private int transactionErrorCode = Transaction.ERROR_NONE;
|
||||
/**
|
||||
* ReflectionTransactionError constructor.
|
||||
*/
|
||||
public ReflectionTransactionError() {
|
||||
}//ReflectionTransactionError()//
|
||||
/**
|
||||
* ReflectionTransactionError constructor.
|
||||
* @param transactionErrorCode The transaction error code identifier (Transaction.ERROR_xxx).
|
||||
*/
|
||||
public ReflectionTransactionError(int transactionErrorCode) {
|
||||
this.transactionErrorCode = transactionErrorCode;
|
||||
}//ReflectionTransactionError()//
|
||||
/**
|
||||
* Gets the transaction system error code detailing the problem.
|
||||
* @return The transaction error code identifier (Transaction.ERROR_xxx).
|
||||
*/
|
||||
public int getTransactionErrorCode() {
|
||||
return transactionErrorCode;
|
||||
}//getTransactionErrorCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ReflectionResult#getResultType()
|
||||
*/
|
||||
public int getResultType() {
|
||||
return RESULT_TYPE_TRANSACTIONAL_ERROR;
|
||||
}//getResultType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
transactionErrorCode = in.readInt();
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(transactionErrorCode);
|
||||
}//writeExternal()//
|
||||
}//ReflectionTransactionError//
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (c) 2003,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.controller;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.common.debug.*;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.foundation.attribute.ReflectionContext;
|
||||
import com.foundation.view.*;
|
||||
|
||||
/**
|
||||
* Provides extra support for remote views where the display occurs on a remote process from the model and controller code.
|
||||
*/
|
||||
public abstract class RemoteViewController extends AbstractViewController {
|
||||
/** The remote view context is a reference to the object supporting just this view (versus the view context which supports a system of remote views across a single connection). This must be null if this view is open inside another view. */
|
||||
private IRemoteViewContext remoteViewContext = null;
|
||||
/**
|
||||
* RemoteViewController constructor.
|
||||
* @param viewContext The view's context under which it will operate.
|
||||
*/
|
||||
public RemoteViewController(IViewContext viewContext) {
|
||||
super(viewContext);
|
||||
}//RemoteViewController()//
|
||||
/**
|
||||
* RemoteViewController constructor.
|
||||
* @param viewContext The view's context under which it will operate.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
*/
|
||||
public RemoteViewController(IViewContext context, ReflectionContext reflectionContext) {
|
||||
super(context, reflectionContext);
|
||||
}//RemoteViewController()//
|
||||
/**
|
||||
* RemoteViewController constructor.
|
||||
* @param viewContext The view's context under which it will operate.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is true by default.
|
||||
*/
|
||||
public RemoteViewController(IViewContext viewContext, boolean validateOnOpen) {
|
||||
super(viewContext, validateOnOpen);
|
||||
}//RemoteViewController()//
|
||||
/**
|
||||
* RemoteViewController constructor.
|
||||
* @param viewContext The view's context under which it will operate.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is true by default.
|
||||
*/
|
||||
public RemoteViewController(IViewContext context, ReflectionContext reflectionContext, boolean validateOnOpen) {
|
||||
super(context, reflectionContext, validateOnOpen);
|
||||
}//RemoteViewController()//
|
||||
/**
|
||||
* Gets the remote view context under which this view controller is operating.
|
||||
* @return The view context specific to this remote view controller.
|
||||
*/
|
||||
public IRemoteViewContext getRemoteViewContext() {
|
||||
//The remote view context will be null if this view is open inside another view.//
|
||||
return remoteViewContext != null ? remoteViewContext : ((RemoteViewController) getParentComponent().getController()).getRemoteViewContext();
|
||||
}//getRemoteViewContext()//
|
||||
/**
|
||||
* Creates the controller's primary view component.
|
||||
* @return This view controller's new view object.
|
||||
*/
|
||||
protected IView createView() {
|
||||
Class viewClass = getViewClass();
|
||||
IView result = null;
|
||||
|
||||
if(viewClass == null) {
|
||||
Debug.log("Error: getViewClass() must return a valid view class in the remote view controller " + getClass().getName());
|
||||
}//if//
|
||||
|
||||
try {
|
||||
if(getParentComponent() == null) {
|
||||
Constructor constructor = viewClass.getConstructor(new Class[] {RemoteViewController.class});
|
||||
|
||||
result = (IView) constructor.newInstance(new Object[] {this});
|
||||
}//if//
|
||||
else {
|
||||
Constructor constructor = viewClass.getConstructor(new Class[] {RemoteViewController.class, IView.class});
|
||||
|
||||
result = (IView) constructor.newInstance(new Object[] {this, getParentComponent()});
|
||||
}//else//
|
||||
}//try//
|
||||
catch(NoSuchMethodException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(InvocationTargetException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(InstantiationException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(IllegalAccessException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//createView()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.AbstractViewController#internalClose(com.foundation.view.IView, java.lang.Runnable, java.lang.Runnable)
|
||||
*/
|
||||
protected void internalClose(IView view, Runnable onCloseHandler, Runnable onDisposeHandler) {
|
||||
IRemoteViewContext viewContext = null;
|
||||
|
||||
synchronized(this) {
|
||||
viewContext = getParentComponent() == null ? this.remoteViewContext : null;
|
||||
this.remoteViewContext = null;
|
||||
}//synchronized//
|
||||
|
||||
//Hide the view and call the on close handler.//
|
||||
view.setIsVisible(false); //TODO: Can this be enclosed in the message hold area below?//
|
||||
|
||||
if(onCloseHandler != null) {
|
||||
onCloseHandler.run();
|
||||
}//if//
|
||||
|
||||
//Release the view on the client.//
|
||||
view.addMessageHold();
|
||||
view.viewReleaseAll();
|
||||
postClose();
|
||||
view.removeMessageHold();
|
||||
|
||||
if(viewContext != null && viewContext.isValid()) {
|
||||
//Notify the session view controller to release its self.//
|
||||
viewContext.close();
|
||||
}//if//
|
||||
|
||||
if(onDisposeHandler != null) {
|
||||
onDisposeHandler.run();
|
||||
}//if//
|
||||
}//internalClose()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.AbstractViewController#internalInitialize()
|
||||
*/
|
||||
protected IView internalInitialize() {
|
||||
IView result = null;
|
||||
|
||||
if(getContext() != null) {
|
||||
remoteViewContext.incrementMessageHoldCount();
|
||||
result = createView();
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: Cannot open the view without a proper view context.");
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//internalInitialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.AbstractViewController#internalInitializeContext(com.foundation.view.IViewContext)
|
||||
*/
|
||||
protected void internalInitializeContext(IViewContext context) {
|
||||
if((context != null) && (context instanceof IRemoteSessionContext)) {
|
||||
//This creates a new remote session context which is a proxy to an object on the client that represents the view.//
|
||||
//This should not be done for inner views (views that exist inside other views). Instead those views should use their parent's context.//
|
||||
remoteViewContext = ((IRemoteSessionContext) context).createViewContext(this);
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: Cannot open the view without a proper view context.");
|
||||
}//else//
|
||||
}//internalInitializeContext()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.AbstractViewController#internalOpen(IView)
|
||||
*/
|
||||
protected void internalOpen(final IView view) {
|
||||
view.execute(new com.common.thread.IRunnable() {
|
||||
public Object run() {
|
||||
view.suspendLayouts();
|
||||
view.viewInitializeAll();
|
||||
view.resumeLayouts();
|
||||
view.layout();
|
||||
view.pack();
|
||||
postOpenInitialization(view);
|
||||
|
||||
if(validateOnOpen()) {
|
||||
validate();
|
||||
}//if//
|
||||
|
||||
remoteViewContext.decrementMessageHoldCount();
|
||||
|
||||
return null;
|
||||
}//run()//
|
||||
});
|
||||
}//internalOpen()//
|
||||
/**
|
||||
* Opens a partial view given the view's parent view component reference and an optional view context.
|
||||
* <p>This is intended only to be called by the containing view when it requires the sub-component to be created.</p>
|
||||
* <p><b>Warning: This method is not thread safe. It is assumed that the view will be owned by one parent component and will be opened only by that component.</b></p>
|
||||
* @param parent The non-null parent view component passed by the view.
|
||||
* @param context The context for the view. The actual value is dependant upon the view framework being used. In a remote view this value may be the session context for example.
|
||||
* @return A view associated with this view controller that is a part of another view.
|
||||
*/
|
||||
public IView openPartial(IView parent, Object context) {
|
||||
IView view = null;
|
||||
|
||||
if(parent == null) {
|
||||
throw new RuntimeException("Error: Cannot open a partial view without a parent view part.");
|
||||
}//if//
|
||||
else if(context != getContext()) {
|
||||
throw new RuntimeException("Error: Invalid view context. This view was initialized with a different context than it is being used under.");
|
||||
}//else if//
|
||||
|
||||
if(getView() == null) {
|
||||
setOptions(0);
|
||||
setParentComponent(parent);
|
||||
setView(view = createView());
|
||||
view.addMessageHold();
|
||||
view.suspendLayouts();
|
||||
getDecorationManager().initialize(getView());
|
||||
isOpen(true);
|
||||
view.setController(this);
|
||||
view.viewInitializeAll();
|
||||
view.resumeLayouts();
|
||||
view.layout();
|
||||
//view.pack();
|
||||
//postOpenInitialization(view);
|
||||
view.removeMessageHold();
|
||||
validate();
|
||||
}//if//
|
||||
|
||||
return view;
|
||||
}//openPartialView()//
|
||||
/**
|
||||
* Executes the runnable on the view's event thread.
|
||||
* <p>It is necessary to run most view related operations on the view thread since all views and all associated model and controller objects are not thread safe.</p>
|
||||
*/
|
||||
public final Object execute(IRunnable runnable) {
|
||||
return getRemoteViewContext().execute(runnable);
|
||||
}//execute()//
|
||||
/**
|
||||
* Executes the runnable on the view's event thread and returns immediatly.
|
||||
* <p>It is necessary to run most view related operations on the view thread since all views and all associated model and controller objects are not thread safe.</p>
|
||||
*/
|
||||
public final void executeAsync(IRunnable runnable) {
|
||||
getRemoteViewContext().executeAsync(runnable);
|
||||
}//executeAsync()//
|
||||
/**
|
||||
* Gets the display metadata which describes the client view system such as information about the display locations and sizes.
|
||||
* @return The metadata describing the client view system.
|
||||
*/
|
||||
public ViewSystemMetadata getViewSystemMetadata() {
|
||||
return getRemoteViewContext().getSessionContext().getViewSystemMetadata();
|
||||
}//getViewSystemMetadata()//
|
||||
}//RemoteViewController//
|
||||
@@ -0,0 +1,492 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.orb.Orb;
|
||||
import com.common.util.ICollection;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.attribute.IReflectableCollection;
|
||||
import com.foundation.common.Entity;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.exception.TransactionException;
|
||||
import com.foundation.transaction.ReadTransaction;
|
||||
import com.foundation.transaction.Transaction;
|
||||
import com.foundation.transaction.TransactionContextHolder;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.util.HashMapAdapter;
|
||||
import com.foundation.util.IKeyExtractor;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.util.IManagedList;
|
||||
import com.foundation.util.ManagedList;
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
/**
|
||||
* Manages models on a single process and maintains a list of all models at all times.
|
||||
* This is the simplest of model managers and should be used for small collections of models.
|
||||
*
|
||||
* TODO: *** Ensure that modifying the list via a reflection of it does properly reset the list's change tracking such that a manual add that fails doesn't undo reflection synchronization changes. ***
|
||||
*
|
||||
*/
|
||||
public class SingleListModelManager extends ModelManager implements ISingleListModelManager {
|
||||
public static final Attribute MODELS = registerCollection(SingleListModelManager.class, "models", AO_REFERENCED | AO_LAZY);
|
||||
public static final Attribute MODELS_BY_KEY = registerAttribute(SingleListModelManager.class, "modelsByKey", AO_REFERENCED | AO_LAZY);
|
||||
|
||||
protected class ModelListManager extends ModelController.ModelListManager {
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAdd(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canAdd(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canAdd(value);
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
result &= super.canAdd(managed, value, context);
|
||||
|
||||
return result;
|
||||
}//canAdd()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, IManagedCollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED && values.getSize() > 0) {
|
||||
for(IIterator iterator = values.iterator(); result && iterator.hasNext(); ) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canAdd(iterator.next());
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//TODO: May wish to override the default functionality to use bulk query's and find the super set of added attributes, or group by similar attribute sets.
|
||||
result &= super.canAdd(managed, values, context);
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canAddAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canAddAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED && values.length > 0) {
|
||||
for(int index = 0; result && index < values.length; index++) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canAdd(values[index]);
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//TODO: May wish to override the default functionality to use bulk query's and find the super set of added attributes, or group by similar attribute sets.
|
||||
result &= super.canAdd(managed, values, context);
|
||||
|
||||
return result;
|
||||
}//canAddAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemove(com.common.util.IManagedCollection, java.lang.Object, byte)
|
||||
*/
|
||||
public boolean canRemove(IManagedCollection managed, Object value, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canRemove(value);
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
result &= super.canRemove(managed, value, context);
|
||||
|
||||
return result;
|
||||
}//canRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, com.common.util.IManagedCollection, byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, IManagedCollection values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED && values.getSize() > 0) {
|
||||
for(IIterator iterator = values.iterator(); result && iterator.hasNext(); ) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canRemove(iterator.next());
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//TODO: May wish to override the default functionality to use bulk query's and find the super set of query attributes, or group by similar attribute sets.
|
||||
result &= super.canRemove(managed, values, context);
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.CollectionManager#canRemoveAll(com.common.util.IManagedCollection, java.lang.Object[], byte)
|
||||
*/
|
||||
public boolean canRemoveAll(IManagedCollection managed, Object[] values, byte context) {
|
||||
boolean result = true;
|
||||
|
||||
if(context == CONTEXT_UNTRUSTED && values.length > 0) {
|
||||
for(int index = 0; result && index < values.length; index++) {
|
||||
ReflectionResult reflectionResult = SingleListModelManager.this.canRemove(values[index]);
|
||||
|
||||
if(reflectionResult != null) {
|
||||
result = false;
|
||||
managed.setErrorInfo(reflectionResult);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//TODO: May wish to override the default functionality to use bulk query's and find the super set of query attributes, or group by similar attribute sets.
|
||||
result &= super.canRemove(managed, values, context);
|
||||
|
||||
return result;
|
||||
}//canRemoveAll()//
|
||||
}//ModelListManager//
|
||||
/**
|
||||
* SingleListModelManager constructor.
|
||||
* @param type The type of models being managed.
|
||||
* @param sync Whether to keep the models in sync with other managers in other processes.
|
||||
*/
|
||||
public SingleListModelManager(Class type, boolean sync) {
|
||||
super(type, sync);
|
||||
}//SingleListModelManager()//
|
||||
/**
|
||||
* Called prior to adding a value to the collection. Allows for customization of the add process.
|
||||
* @param value The value about to be added.
|
||||
* @return Result object if the operation should fail. The result should be null if the value CAN be added.
|
||||
*/
|
||||
protected ReflectionResult canAdd(Object value) {
|
||||
//TODO: Override to return a failure result if the object can't be added for what ever reason (generally a constraint error in which case return a ReflectionConstraintError.
|
||||
return null;
|
||||
}//canAdd()//
|
||||
/**
|
||||
* Called prior to adding a value to the collection. Allows for customization of the add process.
|
||||
* @param value The value about to be added.
|
||||
* @return Result object if the operation should fail. The result should be null if the value CAN be added.
|
||||
*/
|
||||
protected ReflectionResult canAdd(IList values) {
|
||||
//TODO: Override to return a failure result if the object can't be added for what ever reason (generally a constraint error in which case return a ReflectionConstraintError.
|
||||
return null;
|
||||
}//canAdd()//
|
||||
/**
|
||||
* Called prior to removing a value to the collection. Allows for customization of the remove process.
|
||||
* @param value The value about to be removed.
|
||||
* @return Result object if the operation should fail. The result should be null if the value CAN be removed.
|
||||
*/
|
||||
protected ReflectionResult canRemove(Object value) {
|
||||
//TODO: Override to return a failure result if the object can't be removed for what ever reason (generally a constraint error in which case return a ReflectionConstraintError.
|
||||
return null;
|
||||
}//canRemove()//
|
||||
/**
|
||||
* Called prior to removing a value to the collection. Allows for customization of the remove process.
|
||||
* @param value The value about to be removed.
|
||||
* @return Result object if the operation should fail. The result should be null if the value CAN be removed.
|
||||
*/
|
||||
protected ReflectionResult canRemove(IList values) {
|
||||
//TODO: Override to return a failure result if the object can't be removed for what ever reason (generally a constraint error in which case return a ReflectionConstraintError.
|
||||
return null;
|
||||
}//canRemove()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#addEntity(com.foundation.common.IEntity, boolean)
|
||||
*/
|
||||
public ReflectionResult addEntity(IEntity entity, boolean returnAddition) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if((result = canAdd(entity)) == null) {
|
||||
if(getModels().add(entity) != -1) {
|
||||
if(returnAddition) {
|
||||
result = new ReflectionSuccess(entity);
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
|
||||
//Clear any change tracking so we don't accidentially reverse the changes in the future.//
|
||||
getModels().resetChangeTracking();
|
||||
}//if//
|
||||
else {
|
||||
//Reverse the changes to the collection.//
|
||||
getModels().reverseObjectChanges();
|
||||
//Convert this error info into a reflection result.//
|
||||
result = ReflectionResult.createResult(getModels().getErrorInfo());
|
||||
}//else//
|
||||
}//if//
|
||||
}//try//
|
||||
catch(TransactionException e) {
|
||||
result = e.getErrorInfo().getTransactionErrorCode() != Transaction.ERROR_NONE ? (ReflectionResult) new ReflectionTransactionError(e.getErrorInfo().getTransactionErrorCode()) : (ReflectionResult) new ReflectionModelError(e.getErrorInfo().getModelErrorCode());
|
||||
}//catch//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//addEntity()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#addEntities(com.common.util.IList, boolean)
|
||||
*/
|
||||
public ReflectionResult addEntities(IList entities, boolean returnAdditions) {
|
||||
ReflectionResult result = null;
|
||||
LiteList results = new LiteList(entities.getSize());
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if((result = canAdd(entities)) == null) {
|
||||
if(getModels().addAll(entities)) {
|
||||
if(returnAdditions) {
|
||||
results.addAll(entities);
|
||||
result = new ReflectionSuccess(results);
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
|
||||
//Clear any change tracking so we don't accidentially reverse the changes in the future.//
|
||||
getModels().resetChangeTracking();
|
||||
}//if//
|
||||
else {
|
||||
//Reverse the changes to the collection.//
|
||||
getModels().reverseObjectChanges();
|
||||
//Convert this error info into a reflection result.//
|
||||
result = ReflectionResult.createResult(getModels().getErrorInfo());
|
||||
}//else//
|
||||
}//if//
|
||||
}//try//
|
||||
catch(TransactionException e) {
|
||||
result = e.getErrorInfo().getTransactionErrorCode() != Transaction.ERROR_NONE ? (ReflectionResult) new ReflectionTransactionError(e.getErrorInfo().getTransactionErrorCode()) : (ReflectionResult) new ReflectionModelError(e.getErrorInfo().getModelErrorCode());
|
||||
}//catch//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//addEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#removeEntity(com.foundation.common.IEntity)
|
||||
*/
|
||||
public ReflectionResult removeEntity(IEntity entity) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if((result = canRemove(entity)) == null) {
|
||||
if(getModels().remove(entity)) {
|
||||
result = new ReflectionSuccess(null);
|
||||
//Clear any change tracking so we don't accidentially reverse the changes in the future.//
|
||||
getModels().resetChangeTracking();
|
||||
}//if//
|
||||
else {
|
||||
//Reverse the changes to the collection.//
|
||||
getModels().reverseObjectChanges();
|
||||
//Convert this error info into a reflection result.//
|
||||
result = ReflectionResult.createResult(getModels().getErrorInfo());
|
||||
}//else//
|
||||
}//if//
|
||||
}//try//
|
||||
catch(TransactionException e) {
|
||||
result = e.getErrorInfo().getTransactionErrorCode() != Transaction.ERROR_NONE ? (ReflectionResult) new ReflectionTransactionError(e.getErrorInfo().getTransactionErrorCode()) : (ReflectionResult) new ReflectionModelError(e.getErrorInfo().getModelErrorCode());
|
||||
}//catch//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//removeEntity()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#removeEntities(com.common.util.IList)
|
||||
*/
|
||||
public ReflectionResult removeEntities(IList entities) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if((result = canRemove(entities)) == null) {
|
||||
if(getModels().removeAll(entities)) {
|
||||
result = new ReflectionSuccess(null);
|
||||
//Clear any change tracking so we don't accidentially reverse the changes in the future.//
|
||||
getModels().resetChangeTracking();
|
||||
}//if//
|
||||
else {
|
||||
//Reverse the changes to the collection.//
|
||||
getModels().reverseObjectChanges();
|
||||
//Convert this error info into a reflection result.//
|
||||
result = ReflectionResult.createResult(getModels().getErrorInfo());
|
||||
}//else//
|
||||
}//if//
|
||||
}//try//
|
||||
catch(TransactionException e) {
|
||||
result = e.getErrorInfo().getTransactionErrorCode() != Transaction.ERROR_NONE ? (ReflectionResult) new ReflectionTransactionError(e.getErrorInfo().getTransactionErrorCode()) : (ReflectionResult) new ReflectionModelError(e.getErrorInfo().getModelErrorCode());
|
||||
}//catch//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//removeEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#getEntities()
|
||||
*/
|
||||
public ReflectionResult getEntities() {
|
||||
return new ReflectionSuccess(getModels());
|
||||
}//getEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#findEntities(java.lang.Object[])
|
||||
*/
|
||||
public ReflectionResult findEntities(Object[] keys) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
IManagedList list = new ManagedList(keys.length);
|
||||
LiteHashMap map = getModelsByKey();
|
||||
|
||||
for(int index = 0; index < keys.length; index++) {
|
||||
IEntity next = (IEntity) map.get(keys[index]);
|
||||
|
||||
if(next != null) {
|
||||
list.add(next);
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
list.resetChangeTracking();
|
||||
result = new ReflectionSuccess(list);
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//getEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleListModelManager#findEntities(com.foundation.transaction.ReadTransaction)
|
||||
*/
|
||||
public ReflectionResult findEntities(ReadTransaction transaction) {
|
||||
ReflectionResult result = null;
|
||||
|
||||
try {
|
||||
Object queryResult = getTransactionService().process(transaction, false);
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if(queryResult instanceof ICollection) {
|
||||
ICollection queryList = (ICollection) queryResult;
|
||||
Object[] keys = new Object[queryList.getSize()];
|
||||
int index = 0;
|
||||
IManagedList list = new ManagedList(keys.length);
|
||||
LiteHashMap map = getModelsByKey();
|
||||
|
||||
//Generate the keys from the query results.//
|
||||
for(IIterator iterator = queryList.iterator(); iterator.hasNext(); index++) {
|
||||
keys[index] = ((IEntity) iterator.next()).entityGetKey();
|
||||
}//for//
|
||||
|
||||
//Search for shared models with the given keys.//
|
||||
for(index = 0; index < keys.length; index++) {
|
||||
IEntity next = (IEntity) map.get(keys[index]);
|
||||
|
||||
if(next != null) {
|
||||
list.add(next);
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
list.resetChangeTracking();
|
||||
result = new ReflectionSuccess(list);
|
||||
}//if//
|
||||
else if(queryResult instanceof IEntity) {
|
||||
Object key = ((IEntity) queryResult).entityGetKey();
|
||||
|
||||
if(key != null) {
|
||||
result = new ReflectionSuccess((IEntity) getModelsByKey().get(key));
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionModelError(IEntity.MODEL_ERROR_KEY_CREATION_FAILED);
|
||||
}//else//
|
||||
}//else//
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
result = new ReflectionTransactionError(transaction.getErrorCode());
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//getEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Entity#lazyLoadAttribute(com.foundation.metadata.Attribute)
|
||||
*/
|
||||
protected Object lazyLoadAttribute(Attribute attribute) {
|
||||
Object result = null;
|
||||
|
||||
if(attribute == MODELS) {
|
||||
try {
|
||||
IManagedList models = new ManagedList(new ModelListManager(), 100);
|
||||
Entity query = (Entity) getType().newInstance();
|
||||
|
||||
if(query.isObjectRepositoryBound()) {
|
||||
result = query.getTransactionService().readMany(query, models);
|
||||
}//if//
|
||||
else {
|
||||
result = models;
|
||||
}//else//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
else if(attribute == MODELS_BY_KEY) {
|
||||
result = new HashMapAdapter(getModels(), new IKeyExtractor() {
|
||||
public Object getKey(Object value) {
|
||||
return ((IEntity) value).entityGetKey();
|
||||
}//getKey()//
|
||||
});
|
||||
}//else if//
|
||||
else {
|
||||
result = super.lazyLoadAttribute(attribute);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//lazyLoadAttribute()//
|
||||
/**
|
||||
* Gets the collection of all models.
|
||||
* @return The models.
|
||||
*/
|
||||
private IManagedList getModels() {
|
||||
return (IManagedList) getAttributeValue(MODELS);
|
||||
}//getModels()//
|
||||
/**
|
||||
* Gets the modelsByKey value.
|
||||
* @return The modelsByKey value.
|
||||
*/
|
||||
private LiteHashMap getModelsByKey() {
|
||||
return (LiteHashMap) getAttributeValue(MODELS_BY_KEY);
|
||||
}//getModelsByKey()//
|
||||
}//SingleListModelManager//
|
||||
@@ -0,0 +1,402 @@
|
||||
package com.foundation.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.comparison.IComparator;
|
||||
import com.common.debug.Debug;
|
||||
import com.common.orb.Orb;
|
||||
import com.common.util.ICollection;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteList;
|
||||
import com.common.util.SoftLiteHashSet;
|
||||
import com.foundation.attribute.IReflectable;
|
||||
import com.foundation.common.CompositeEntityKey;
|
||||
import com.foundation.common.Entity;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
import com.foundation.metadata.TypeMetadata;
|
||||
import com.foundation.transaction.ReadTransaction;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.util.IManagedList;
|
||||
import com.foundation.util.ManagedList;
|
||||
import com.foundation.util.SoftHashMap;
|
||||
|
||||
/**
|
||||
* Manages models on a single process and maintains a map of models currently loaded into memory.
|
||||
* This should be used for large collections of shared models where a small number will be loaded at any given time.
|
||||
*/
|
||||
public class SingleMappedModelManager extends ModelManager implements ISingleMappedModelManager {
|
||||
/** The set of models indexed using the model's hashCode() and equals(Object) methods. The set is soft such that values are GC'd as needed if there isn't a hard reference (such as a proxy to it held by a remote process). */
|
||||
private SoftLiteHashSet set = new SoftLiteHashSet(1000, SoftLiteHashSet.DEFAULT_LOAD_FACTOR, new IComparator() {
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
}//writeExternal()//
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
}//readExternal()//
|
||||
public int hash(Object value) {
|
||||
return value == null ? 0 : value instanceof IEntity ? ((IEntity) value).entityGetKey().hashCode() : value.hashCode();
|
||||
}//hash()//
|
||||
public int compare(Object value1, Object value2) {
|
||||
return (value1 instanceof IEntity && value2 instanceof IEntity ? value1.equals(value2) : value1 instanceof IEntity || value2 instanceof IEntity ? (value1 instanceof IEntity ? value1.equals(value2) : value2.equals(value1)) : Comparator.equals(value1, value2)) ? Comparator.EQUAL : Comparator.NOT_EQUAL;
|
||||
}//compare()//
|
||||
}, SoftLiteHashSet.STYLE_NO_DUPLICATES);
|
||||
/** The cache of the type metadata for the class of model managed. */
|
||||
private TypeMetadata metadata = null;
|
||||
/**
|
||||
* SingleMappedModelManager constructor.
|
||||
* @param type The type of models being managed.
|
||||
* @param sync Whether to keep the models in sync with other managers in other processes.
|
||||
*/
|
||||
public SingleMappedModelManager(Class type, boolean sync) {
|
||||
super(type, sync);
|
||||
metadata = MetadataService.getSingleton().getTypeMetadata(type);
|
||||
}//SingleMappedModelManager()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#addEntities(com.common.util.IList, boolean)
|
||||
*/
|
||||
public ReflectionResult addEntities(IList entities, boolean returnAddition) {
|
||||
ReflectionResult result;
|
||||
IList results = new LiteList(entities.getSize());
|
||||
|
||||
//Add each entity to the repository.//
|
||||
for(int index = 0, size = entities.getSize(); index < size; index++) {
|
||||
Entity entity = (Entity) entities.get(index);
|
||||
TransactionErrorInfo queryResult = entity.entityUpdate();
|
||||
|
||||
if(queryResult == null) {
|
||||
results.add(entity);
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Detect a problem with a repository constraint and report it. This will require some changes to the structure here - to allow both successful and failed results. Perhaps return an array of results?
|
||||
//result = ReflectionResult.createResult(queryResult);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
if(results.getSize() > 0) {
|
||||
lock(this);
|
||||
|
||||
//Ensure each result is in the map.//
|
||||
try {
|
||||
for(int index = 0, size = results.getSize(); index < size; index++) {
|
||||
Entity entity = (Entity) results.get(index);
|
||||
|
||||
//map.put(metadata.getKeyExtractor().getKey(entity), entity);//
|
||||
if(set.add(entity) == -1) {
|
||||
Debug.log(new RuntimeException("Shouldn't ever get here. The entity's hashCode() or equals(Object) method may be broken or undefined."));
|
||||
}//if//
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
result = new ReflectionSuccess(results);
|
||||
|
||||
return result;
|
||||
}//addEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#addEntity(com.foundation.common.IEntity, boolean)
|
||||
*/
|
||||
public ReflectionResult addEntity(IEntity entity, boolean returnAddition) {
|
||||
IReflectable result = null;
|
||||
TransactionErrorInfo queryResult = entity.entityUpdate();
|
||||
|
||||
if(queryResult == null) {
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if(set.add(entity) == -1) {
|
||||
Debug.log(new RuntimeException("Shouldn't ever get here. The entity's hashCode() or equals(Object) method may be broken or undefined."));
|
||||
}//if//
|
||||
|
||||
result = entity;
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return queryResult == null ? new ReflectionSuccess(result) : ReflectionResult.createResult(queryResult);
|
||||
}//addEntity()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#findEntities(java.lang.Object[])
|
||||
*/
|
||||
public ReflectionResult findEntities(Object[] keys) {
|
||||
ReflectionResult result;
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if(keys != null) {
|
||||
LiteList missing = new LiteList(keys.length);
|
||||
IManagedList results = new ManagedList(keys.length);
|
||||
|
||||
//Locate the entities already in memory, and save those keys not in memory for locating later.//
|
||||
for(int index = 0; index < keys.length; index++) {
|
||||
IEntity found = (IEntity) set.get(keys[index]);
|
||||
|
||||
if(found != null) {
|
||||
results.add(found);
|
||||
}//if//
|
||||
else {
|
||||
missing.add(keys[index]);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
//Read the missing entities from the repository and pull them into memory as shared objects.//
|
||||
for(int index = 0; index < missing.getSize(); index++) {
|
||||
Object nextKey = missing.get(index);
|
||||
IEntity instance = (IEntity) metadata.getTypeClass().newInstance();
|
||||
|
||||
//Set the key values from the key.//
|
||||
instance.entitySetKey(nextKey);
|
||||
|
||||
//Read the entity from the repository and place it in the collections.//
|
||||
if(getTransactionService().readSingle(instance, true) != null) {
|
||||
set.add(instance);
|
||||
results.add(instance);
|
||||
}//if//
|
||||
else {
|
||||
Debug.log(new RuntimeException("Failed to read the object from the repository given the object's key."));
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
results.resetChangeTracking();
|
||||
result = new ReflectionSuccess(results);
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
}//try//
|
||||
catch(InstantiationException e) {
|
||||
Debug.log("Failed to create a new instance due to an uncaught exception in the default constructor.", e);
|
||||
result = new ReflectionModelError();
|
||||
}//catch//
|
||||
catch(IllegalAccessException e) {
|
||||
Debug.log("Every entity must have a public default constructor to allow the framework access to creating new instances.", e);
|
||||
result = new ReflectionModelError();
|
||||
}//catch//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//findEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#findEntities(com.foundation.transaction.ReadTransaction)
|
||||
*/
|
||||
public ReflectionResult findEntities(ReadTransaction transaction) {
|
||||
ReflectionResult result;
|
||||
|
||||
try {
|
||||
Object queryResult = getTransactionService().process(transaction, false);
|
||||
|
||||
if(queryResult != null) {
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
if(queryResult instanceof ICollection) {
|
||||
ICollection queryList = (ICollection) queryResult;
|
||||
IManagedList results = new ManagedList(queryList.getSize());
|
||||
|
||||
//Iterate over each result and either place it in the map, or if it already exists in the map then replace it in the result set.//
|
||||
for(IIterator iterator = queryList.iterator(); iterator.hasNext(); ) {
|
||||
IEntity next = (IEntity) iterator.next();
|
||||
Object key = next.entityGetKey();
|
||||
IEntity mapped;
|
||||
|
||||
if(key == null) {
|
||||
Debug.log(new RuntimeException("The class " + next.getClass().getName() + " must identify one or more attributes as being KEY attributes by adding AO_KEY to the attribute definition's options. Example: public static final Attribute ID = registerAttribute(User.class, \"id\", AO_KEY);"));
|
||||
}//if//
|
||||
|
||||
mapped = (IEntity) set.get(key);
|
||||
|
||||
if(mapped != null) {
|
||||
results.add(mapped);
|
||||
}//if//
|
||||
else {
|
||||
set.add(next);
|
||||
results.add(next);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
results.resetChangeTracking();
|
||||
result = new ReflectionSuccess(results);
|
||||
}//if//
|
||||
else if(queryResult instanceof IEntity) {
|
||||
Object key = ((IEntity) queryResult).entityGetKey();
|
||||
IEntity mapped = (IEntity) set.get(key);
|
||||
|
||||
if(mapped != null) {
|
||||
result = new ReflectionSuccess(mapped);
|
||||
}//if//
|
||||
else {
|
||||
set.add(queryResult);
|
||||
result = new ReflectionSuccess(queryResult);
|
||||
}//else//
|
||||
}//else if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
result = new ReflectionTransactionError(transaction.getErrorCode());
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//findEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#findEntities(com.foundation.common.IEntity)
|
||||
*/
|
||||
public ReflectionResult findEntities(IEntity query) {
|
||||
ReflectionResult result;
|
||||
IList results;
|
||||
|
||||
results = (LiteList) query.getTransactionService().readMany(query, new LiteList(100, 1000));
|
||||
|
||||
if(results != null) {
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
//Iterate over each result and either place it in the map, or if it already exists in the map then replace it in the result set.//
|
||||
for(int index = 0, size = results.getSize(); index < size; index++) {
|
||||
IEntity next = (IEntity) results.get(index);
|
||||
Object key = next.entityGetKey();
|
||||
IEntity mapped;
|
||||
|
||||
if(key == null) {
|
||||
Debug.log(new RuntimeException("The class " + next.getClass().getName() + " must identify one or more attributes as being KEY attributes by adding AO_KEY to the attribute definition's options. Example: public static final Attribute ID = registerAttribute(User.class, \"id\", AO_KEY);"));
|
||||
}//if//
|
||||
|
||||
mapped = (IEntity) set.get(key);
|
||||
|
||||
if(mapped != null) {
|
||||
results.set(index, mapped);
|
||||
}//if//
|
||||
else {
|
||||
/*map.put(key, next);*/
|
||||
set.add(next);
|
||||
results.set(index, next);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
result = new ReflectionSuccess(results);
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//findEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#findEntity(com.foundation.common.IEntity)
|
||||
*/
|
||||
public ReflectionResult findEntity(IEntity query) {
|
||||
ReflectionResult result;
|
||||
IEntity entity;
|
||||
|
||||
//TODO: If the key is already set then just use that.
|
||||
entity = (IEntity) query.getTransactionService().readSingle(query, false);
|
||||
|
||||
if(entity != null) {
|
||||
Object key = entity.entityGetKey();
|
||||
|
||||
if(key == null) {
|
||||
Debug.log(new RuntimeException("The class " + entity.getClass().getName() + " must identify one or more attributes as being KEY attributes by adding AO_KEY to the attribute definition's options. Example: public static final Attribute ID = registerAttribute(User.class, \"id\", AO_KEY);"));
|
||||
}//if//
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
IEntity mapped = (IEntity) set.get(key);
|
||||
|
||||
if(mapped != null) {
|
||||
entity = mapped;
|
||||
result = new ReflectionSuccess(entity);
|
||||
}//if//
|
||||
else {
|
||||
//map.put(key, result);
|
||||
set.add(entity);
|
||||
result = new ReflectionSuccess(entity);
|
||||
}//else//
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
}//if//
|
||||
else {
|
||||
result = new ReflectionSuccess(null);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//findEntity()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#removeEntities(com.common.util.IList)
|
||||
*/
|
||||
public ReflectionResult removeEntities(IList entities) {
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
//Iterate over the entities and remove them one at a time.//
|
||||
for(int index = 0, size = entities.getSize(); index < size; index++) {
|
||||
Object next = entities.get(index);
|
||||
IEntity entity = (IEntity) (Orb.isProxy(next) && Orb.isLocal(next) ? Orb.getLocal(next) : next);
|
||||
Object key = entity.entityGetKey();
|
||||
|
||||
if(key == null) {
|
||||
Debug.log(new RuntimeException("The class " + entity.getClass().getName() + " must identify one or more attributes as being KEY attributes by adding AO_KEY to the attribute definition's options. Example: public static final Attribute ID = registerAttribute(User.class, \"id\", AO_KEY);"));
|
||||
}//if//
|
||||
|
||||
//Note: We don't care if they were in the map, but they should be.//
|
||||
//map.remove(key);
|
||||
set.remove(key);
|
||||
entity.entityDelete();
|
||||
}//for//
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return new ReflectionSuccess(null);
|
||||
}//removeEntities()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.ISingleMappedModelManager#removeEntity(com.foundation.common.IEntity)
|
||||
*/
|
||||
public ReflectionResult removeEntity(IEntity entity) {
|
||||
Object key = entity.entityGetKey();
|
||||
|
||||
if(key == null) {
|
||||
Debug.log(new RuntimeException("The class " + entity.getClass().getName() + " must identify one or more attributes as being KEY attributes by adding AO_KEY to the attribute definition's options. Example: public static final Attribute ID = registerAttribute(User.class, \"id\", AO_KEY);"));
|
||||
}//if//
|
||||
|
||||
lock(this);
|
||||
|
||||
try {
|
||||
//Note: We don't care if it is in the map, but it should be.//
|
||||
//map.remove(key);
|
||||
set.remove(key);
|
||||
((IEntity) entity).entityDelete();
|
||||
}//try//
|
||||
finally {
|
||||
unlock(this);
|
||||
}//finally//
|
||||
|
||||
return new ReflectionSuccess(null);
|
||||
}//removeEntity()//
|
||||
}//SingleMappedModelManager//
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 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.controller;
|
||||
|
||||
/**
|
||||
* Thrown by the view controller when the view has been closed.
|
||||
*/
|
||||
public class ViewClosedException extends Exception {
|
||||
/**
|
||||
* ViewClosedException constructor.
|
||||
*/
|
||||
public ViewClosedException() {
|
||||
}//ViewClosedException()//
|
||||
/**
|
||||
* ViewClosedException constructor.
|
||||
* @param message
|
||||
*/
|
||||
public ViewClosedException(String message) {
|
||||
super(message);
|
||||
}//ViewClosedException()//
|
||||
/**
|
||||
* ViewClosedException constructor.
|
||||
* @param cause
|
||||
*/
|
||||
public ViewClosedException(Throwable cause) {
|
||||
super(cause);
|
||||
}//ViewClosedException()//
|
||||
/**
|
||||
* ViewClosedException constructor.
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public ViewClosedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}//ViewClosedException()//
|
||||
}//ViewClosedException//
|
||||
95
Foundation/src/com/foundation/controller/ViewController.java
Normal file
95
Foundation/src/com/foundation/controller/ViewController.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2008 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.controller;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.common.debug.*;
|
||||
import com.foundation.view.*;
|
||||
import com.foundation.attribute.*;
|
||||
|
||||
/**
|
||||
* The view controller manages the interaction between the view and the model.
|
||||
* Each view controller will control exactly one view or partial view.
|
||||
* A view may be modal or partial in which case the parent view controller will be set.
|
||||
* A view controller having a partial view will not open or close the view directly, but will instead rely on the containing view to open and close the view.
|
||||
*/
|
||||
public abstract class ViewController extends AbstractViewController {
|
||||
/**
|
||||
* ViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
*/
|
||||
public ViewController(IViewContext context) {
|
||||
super(context);
|
||||
}//ViewController()//
|
||||
/**
|
||||
* ViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
*/
|
||||
public ViewController(IViewContext context, ReflectionContext reflectionContext) {
|
||||
super(context, reflectionContext);
|
||||
}//ViewController()//
|
||||
/**
|
||||
* ViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is true by default.
|
||||
*/
|
||||
public ViewController(IViewContext context, boolean validateOnOpen) {
|
||||
super(context, validateOnOpen);
|
||||
}//ViewController()//
|
||||
/**
|
||||
* ViewController constructor.
|
||||
* @param context The context underwhich the view is being created. This context manages the threading, resources, and request handling for the view as well as ties together related views.
|
||||
* @param reflectionContext The reflection context to be used by the view.
|
||||
* @param validateOnOpen Whether the view should perform validation immediately after opening. This is true by default.
|
||||
*/
|
||||
public ViewController(IViewContext context, ReflectionContext reflectionContext, boolean validateOnOpen) {
|
||||
super(context, reflectionContext, validateOnOpen);
|
||||
}//ViewController()//
|
||||
/**
|
||||
* Creates the controller's primary view component.
|
||||
* @return This view controller's new view object.
|
||||
*/
|
||||
protected IView createView() {
|
||||
Class viewClass = getViewClass();
|
||||
IView result = null;
|
||||
|
||||
if(viewClass == null) {
|
||||
Debug.log("Error: getViewClass() must return a valid view class in the view controller " + getClass().getName());
|
||||
}//if//
|
||||
|
||||
try {
|
||||
if(getParentComponent() == null) {
|
||||
Constructor constructor = viewClass.getConstructor(new Class[] {ViewController.class});
|
||||
|
||||
result = (IView) constructor.newInstance(new Object[] {this});
|
||||
}//if//
|
||||
else {
|
||||
Constructor constructor = viewClass.getConstructor(new Class[] {ViewController.class, IView.class});
|
||||
|
||||
result = (IView) constructor.newInstance(new Object[] {this, getParentComponent()});
|
||||
}//else//
|
||||
}//try//
|
||||
catch(NoSuchMethodException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(InvocationTargetException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(InstantiationException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(IllegalAccessException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//createView()//
|
||||
}//ViewController//
|
||||
282
Foundation/src/com/foundation/event/CustomRequestHandler.java
Normal file
282
Foundation/src/com/foundation/event/CustomRequestHandler.java
Normal file
@@ -0,0 +1,282 @@
|
||||
package com.foundation.event;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.common.thread.ISingleThreadedContext;
|
||||
import com.common.thread.ThreadService;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/**
|
||||
* This request handler is designed to not have a dedicated thread for processing incoming runnables.
|
||||
* Instead of a dedicated thread, this handler uses the calling thread for synchronous calls, and a reusable thread for asynchronous calls.
|
||||
* Since there isn't a dedicated thread, the handler does not ever need to be stopped or started.
|
||||
*/
|
||||
public class CustomRequestHandler implements IRequestHandler, ISingleThreadedContext {
|
||||
private volatile Thread requestThread = null;
|
||||
private LiteList requestQueue = new LiteList(10);
|
||||
/** Whether to allow potential deadlock situations. */
|
||||
private boolean allowPotentialDeadlockSituations = false;
|
||||
|
||||
/**
|
||||
* Used to kick off the next queued runnable (or request) in a reusable thread.
|
||||
* The reusable thread will keep processing queued runnables until none are left or a synchronous runnable is encountered.
|
||||
*/
|
||||
private class AsyncRunnable implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
IRunnable runnable = null;
|
||||
|
||||
synchronized(CustomRequestHandler.this) {
|
||||
//Make this thread the requestThread.//
|
||||
requestThread = Thread.currentThread();
|
||||
//Flag this thread as running in this reflection context.//
|
||||
ThreadService.setIsInSingleThreadedContext(CustomRequestHandler.this);
|
||||
|
||||
//Get the runnable.//
|
||||
try {
|
||||
runnable = (IRunnable) requestQueue.remove(0);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//synchronized//
|
||||
|
||||
while(runnable != null) {
|
||||
//Run the runnable.//
|
||||
try {
|
||||
runnable.run();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
synchronized(CustomRequestHandler.this) {
|
||||
if(requestQueue.getSize() > 0) {
|
||||
IRunnable next = (IRunnable) requestQueue.get(0);
|
||||
|
||||
if(next instanceof SynchronousRunnable) {
|
||||
//Kick off the thread waiting to run the next runnable.//
|
||||
((SynchronousRunnable) next).notifyOnQueue();
|
||||
//Exit the loop and return the thread.//
|
||||
runnable = null;
|
||||
//Clear the single threaded context flag.//
|
||||
ThreadService.setIsInSingleThreadedContext(null);
|
||||
//Clear the current thread as the request thread.//
|
||||
requestThread = null;
|
||||
}//if//
|
||||
else {
|
||||
runnable = next;
|
||||
requestQueue.remove(0);
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//Exit the loop and return the thread.//
|
||||
runnable = null;
|
||||
//Clear the single threaded context flag.//
|
||||
ThreadService.setIsInSingleThreadedContext(null);
|
||||
//Clear the current thread as the request thread.//
|
||||
requestThread = null;
|
||||
}//else//
|
||||
}//synchronized//
|
||||
}//while//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
//Clear the single threaded context flag.//
|
||||
ThreadService.setIsInSingleThreadedContext(null);
|
||||
|
||||
synchronized(CustomRequestHandler.this) {
|
||||
requestThread = null;
|
||||
}//synchronized//
|
||||
}//catch//
|
||||
}//run()//
|
||||
}//AsyncRunnable//
|
||||
|
||||
/**
|
||||
* A simple runnable that wrappers another runnable and notifies waiting threads when the execution is complete.
|
||||
*/
|
||||
private static class SynchronousRunnable implements IRunnable {
|
||||
private IRunnable runnable = null;
|
||||
private boolean isReady = false;
|
||||
|
||||
/**
|
||||
* SynchronousRunnable constructor.
|
||||
* @param runnable The runnable that will be executed and whose result will be passed to waiting threads.
|
||||
*/
|
||||
public SynchronousRunnable(IRunnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}//SynchronousRunnable()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.thread.IRunnable#run()
|
||||
*/
|
||||
public Object run() {
|
||||
return runnable.run();
|
||||
}//run()//
|
||||
public void notifyOnQueue() {
|
||||
synchronized(this) {
|
||||
isReady = true;
|
||||
notify();
|
||||
}//synchronized//
|
||||
}//notifyOnQueue()//
|
||||
public void waitOnQueue() {
|
||||
synchronized(this) {
|
||||
while(!isReady) {
|
||||
try {
|
||||
wait();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
}//while//
|
||||
}//synchronized//
|
||||
}//waitOnQueue()//
|
||||
}//SynchronousRunnable//
|
||||
/**
|
||||
* CustomRequestHandler constructor.
|
||||
*/
|
||||
public CustomRequestHandler() {
|
||||
}//CustomRequestHandler()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IRequestHandler#isRunning()
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
//Will always be true because this request handler does not have a dedicated thread there is never a need to stop it.//
|
||||
return true;
|
||||
}//isRunning()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IRequestHandler#execute(com.common.thread.IRunnable, boolean)
|
||||
*/
|
||||
public Object execute(IRunnable runnable, boolean synchronous) {
|
||||
Object result = null;
|
||||
boolean runIt = false;
|
||||
boolean runImmediately = false;
|
||||
Thread thisThread = Thread.currentThread();
|
||||
ISingleThreadedContext oldContext = null;
|
||||
|
||||
synchronized(this) {
|
||||
//If another thread isn't busy using the context then start immediately.//
|
||||
if(requestThread == null && requestQueue.getSize() == 0) {
|
||||
if(synchronous) {
|
||||
if((oldContext = ThreadService.getSingleThreadedContext()) != null) {
|
||||
if(!allowPotentialDeadlockSituations) {
|
||||
throw new RuntimeException("Warning: Potential deadlock scenario detected. A thread should not be the processing thread for more than one IRequestHandler, or any other single threaded context.");
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
//Make this thread the requestThread.//
|
||||
requestThread = thisThread;
|
||||
//Flag this thread as running in this reflection context.//
|
||||
ThreadService.setIsInSingleThreadedContext(this);
|
||||
//Flag to run below.//
|
||||
runIt = true;
|
||||
}//if//
|
||||
else {
|
||||
//Queue the runnable.//
|
||||
requestQueue.add(runnable);
|
||||
runnable = null;
|
||||
//Run the runnable in a reusable thread.//
|
||||
ThreadService.run(new AsyncRunnable());
|
||||
}//else//
|
||||
}//if//
|
||||
else if(requestThread == thisThread && synchronous) {
|
||||
runImmediately = true;
|
||||
}//else if//
|
||||
else {
|
||||
//Queue the runnable.//
|
||||
if(synchronous) {
|
||||
requestQueue.add(runnable = new SynchronousRunnable(runnable));
|
||||
//Flag to run below.//
|
||||
runIt = true;
|
||||
}//if//
|
||||
else {
|
||||
requestQueue.add(runnable);
|
||||
}//else//
|
||||
}//else//
|
||||
}//synchronized//
|
||||
|
||||
//Make sure to run synchronous tasks that are initiated by the request thread without any of the hoopla.//
|
||||
if(runImmediately) {
|
||||
try {
|
||||
result = runnable.run();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
//If we need to run the runnable, or wait and then run.//
|
||||
if(runIt) {
|
||||
//If we must wait to process the runnable later then do that now.//
|
||||
if(runnable instanceof SynchronousRunnable) {
|
||||
//Wait our turn to run.//
|
||||
((SynchronousRunnable) runnable).waitOnQueue();
|
||||
|
||||
if((oldContext = ThreadService.getSingleThreadedContext()) != null) {
|
||||
if(!allowPotentialDeadlockSituations) {
|
||||
throw new RuntimeException("Warning: Potential deadlock scenario detected. A thread should not be the processing thread for more than one IRequestHandler, or any other single threaded context.");
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
//Set the single threaded context flag.//
|
||||
ThreadService.setIsInSingleThreadedContext(this);
|
||||
|
||||
synchronized(this) {
|
||||
//Make this thread the requestThread.//
|
||||
requestThread = Thread.currentThread();
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
//Run the runnable and collect the result.//
|
||||
try {
|
||||
result = runnable.run();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
//Clear the single threaded context flag (or set it to the old context).//
|
||||
ThreadService.setIsInSingleThreadedContext(oldContext);
|
||||
|
||||
//Start the next queued runnable if there is one.//
|
||||
synchronized(this) {
|
||||
//Clear the current thread as the request thread.//
|
||||
requestThread = null;
|
||||
|
||||
if(requestQueue.getSize() > 0) {
|
||||
IRunnable next = (IRunnable) requestQueue.get(0);
|
||||
|
||||
if(next instanceof SynchronousRunnable) {
|
||||
//Kick off the thread waiting to run the next runnable.//
|
||||
((SynchronousRunnable) next).notifyOnQueue();
|
||||
//Remove the runnable from the queue.//
|
||||
requestQueue.remove(0);
|
||||
}//if//
|
||||
else {
|
||||
//Run the next runnable in a reusable thread.//
|
||||
ThreadService.run(new AsyncRunnable());
|
||||
}//else//
|
||||
}//if//
|
||||
}//synchronized//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//execute()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IRequestHandler#executeAsync(com.common.thread.IRunnable)
|
||||
*/
|
||||
public void executeAsync(IRunnable runnable) {
|
||||
execute(runnable, false);
|
||||
}//executeAsync()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IRequestHandler#execute(com.common.thread.IRunnable)
|
||||
*/
|
||||
public Object execute(IRunnable runnable) {
|
||||
return execute(runnable, true);
|
||||
}//execute()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IRequestHandler#isRequestThread()
|
||||
*/
|
||||
public synchronized boolean isRequestThread() {
|
||||
return Thread.currentThread() == requestThread;
|
||||
}//isRequestThread()//
|
||||
}//CustomRequestHandler//
|
||||
131
Foundation/src/com/foundation/event/EventNotifier.java
Normal file
131
Foundation/src/com/foundation/event/EventNotifier.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
import com.common.orb.*;
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
|
||||
public class EventNotifier {
|
||||
/** The one and only notifier instance. */
|
||||
private static final EventNotifier singleton = new EventNotifier();
|
||||
/** Save a reference to the thread in case we wish to change its priority or name. */
|
||||
private static final Thread notifierThread;
|
||||
/** A queue containing event data. */
|
||||
private Queue eventQueue = new Queue(50);
|
||||
/** A flag used to stop the notifier thread when shutting down. */
|
||||
private boolean stop = false;
|
||||
|
||||
static {
|
||||
notifierThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
singleton.run();
|
||||
}//run()//
|
||||
});
|
||||
notifierThread.setName("Event Notifier");
|
||||
notifierThread.start();
|
||||
}//static//
|
||||
|
||||
/**
|
||||
* Encapsulates a single event's data.
|
||||
*/
|
||||
private static class EventData {
|
||||
private IEventEmitter emitter = null;
|
||||
private int number = -1;
|
||||
private Object[] parameters = null;
|
||||
private ICollection listeners = null;
|
||||
private boolean makeAynchronousRemoteCalls = false;
|
||||
private int flags = 0;
|
||||
private EventData() {
|
||||
}//EventData()//
|
||||
private EventData(IEventEmitter emitter, int number, Object[] parameters, ICollection listeners, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
this.emitter = emitter;
|
||||
this.number = number;
|
||||
this.parameters = parameters;
|
||||
this.listeners = listeners;
|
||||
this.makeAynchronousRemoteCalls = makeAynchronousRemoteCalls;
|
||||
this.flags = flags;
|
||||
}//EventData()//
|
||||
}//EventData()/
|
||||
/**
|
||||
* EventNotifier constructor.
|
||||
*/
|
||||
private EventNotifier() {
|
||||
super();
|
||||
}//EventNotifier()//
|
||||
/**
|
||||
* Gets the one and only event notifier instance.
|
||||
* @return The singleton reference to the event notifier.
|
||||
*/
|
||||
public static EventNotifier getSingleton() {
|
||||
return singleton;
|
||||
}//getSingleton()//
|
||||
/**
|
||||
* Manages an event such that it will be processed in order that is was fired relative to all other events on this process.
|
||||
* <p>This method should only be called by the event support class.</p>
|
||||
*/
|
||||
void manageEvent(IEventEmitter emitter, int number, Object[] parameters, ICollection listeners, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
synchronized(eventQueue) {
|
||||
eventQueue.enqueue(new EventData(emitter, number, parameters, listeners, makeAynchronousRemoteCalls, flags));
|
||||
eventQueue.notify();
|
||||
}//synchronized//
|
||||
}//manageEvent()//
|
||||
/**
|
||||
* Runs the event notifier which will handle executing each event in order of its occurance.
|
||||
*/
|
||||
private void run() {
|
||||
try {
|
||||
while((eventQueue.getSize() > 0) || (!stop)) {
|
||||
EventData eventData = null;
|
||||
|
||||
synchronized(eventQueue) {
|
||||
while((!stop) && (eventQueue.getSize() == 0)) {
|
||||
eventQueue.wait(10000);
|
||||
}//while//
|
||||
|
||||
if(eventQueue.getSize() > 0) {
|
||||
eventData = (EventData) eventQueue.dequeue();
|
||||
}//if//
|
||||
}//synchronized//
|
||||
|
||||
if(eventData != null) {
|
||||
try {
|
||||
IIterator iterator = eventData.listeners.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
IHandler handler = (IHandler) iterator.next();
|
||||
|
||||
if((eventData.makeAynchronousRemoteCalls) && (Orb.isProxy(handler))) {
|
||||
Orb.setOneWayCall(handler);
|
||||
}//if//
|
||||
|
||||
if(handler instanceof IEventHandler) {
|
||||
((IEventHandler) handler).evaluate(Orb.isLocal(handler) ? eventData.emitter : (IEventEmitter) Orb.getProxy(eventData.emitter, IEventEmitter.class), eventData.number, eventData.parameters, eventData.flags);
|
||||
}//if//
|
||||
else {
|
||||
handler.evaluate(eventData.number, eventData.parameters, eventData.flags);
|
||||
}//else//
|
||||
}//while//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e, "Caught while executing an event handler.");
|
||||
}//catch//
|
||||
}//if//
|
||||
}//while//
|
||||
}//try//
|
||||
catch(InterruptedException e) {
|
||||
//Ignore. Let the thread die.//
|
||||
}//catch//
|
||||
}//run()//
|
||||
/**
|
||||
* Stops the event notifier thread if it is running.
|
||||
*/
|
||||
public void stop() {
|
||||
stop = true;
|
||||
}//stop()//
|
||||
}//EventNotifier//
|
||||
897
Foundation/src/com/foundation/event/EventSupport.java
Normal file
897
Foundation/src/com/foundation/event/EventSupport.java
Normal file
@@ -0,0 +1,897 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
import com.common.orb.*;
|
||||
import com.common.util.*;
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.debug.*;
|
||||
import com.common.util.optimized.*;
|
||||
import com.foundation.metadata.Event;
|
||||
import com.foundation.metadata.MetadataService;
|
||||
|
||||
/*
|
||||
* Supports any class implementing the IEventEmitter interface.
|
||||
* <p>This class is serializable, but it is intended only for moving an object from one VM to another via an Orb.</p>
|
||||
*/
|
||||
public class EventSupport implements java.io.Externalizable {
|
||||
/** Whether the event is due to a change that was trusted. A trusted change is one that altered data to be what it should have been all along. A lazy load is an example of a trusted change. */
|
||||
public static final int FLAG_TRUSTED_CHANGE = 0x01;
|
||||
/** Whether the event is due to a change in the underlying original value. So the current value hasn't changed, but an update to the original value has occured. This is used with attributes that have the concept of an original value and a current value. */
|
||||
public static final int FLAG_ORIGINAL_VALUE_CHANGED = 0x02;
|
||||
/** Whether the event is due to a change in the underlying original value. So the current value hasn't changed, but an update to the original value has occured. This is used with attributes that have the concept of an original value and a current value. */
|
||||
public static final int FLAG_ORIGINAL_VALUE_CLEARED = 0x04;
|
||||
/** Whether the event is due to the reflected object updating the object firing the event. */
|
||||
public static final int FLAG_REFLECTION_UPDATE = 0x08;
|
||||
|
||||
/** The event emitter being supported. */
|
||||
private IEventEmitter supportedObject = null;
|
||||
/** A mapping of ListenerSets indexed by the event number. */
|
||||
private IntObjectHashMap threadedListenerMap = new IntObjectHashMap(20);
|
||||
/** A mapping of event handler by number maps indexed by the emitter of the event. */
|
||||
private IHashMap emitterMap = new LiteHashMap(10);
|
||||
|
||||
/**
|
||||
* Encapsulates inlined and threaded listener sets for a specific event.
|
||||
*/
|
||||
private static class ListenerSets {
|
||||
private IHashSet inlinedListeners = null;
|
||||
private IHashSet threadedListeners = null;
|
||||
|
||||
public ListenerSets() {
|
||||
}//ListenerSets()//
|
||||
public void addListener(IHandler listener, boolean inlined) {
|
||||
if(((inlinedListeners == null) || (!inlinedListeners.containsValue(listener))) && ((threadedListeners == null) || (!threadedListeners.containsValue(listener)))) {
|
||||
if(inlined) {
|
||||
if(inlinedListeners == null) {
|
||||
inlinedListeners = new LiteHashSet(8, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getIdentityComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
|
||||
}//if//
|
||||
|
||||
inlinedListeners.add(listener);
|
||||
}//if//
|
||||
else {
|
||||
if(threadedListeners == null) {
|
||||
threadedListeners = new LiteHashSet(8, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getIdentityComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
|
||||
}//if//
|
||||
|
||||
threadedListeners.add(listener);
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Error: Duplicate event listener added to the listener set!", new RuntimeException(), true);
|
||||
}//else//
|
||||
}//addListener()//
|
||||
public void removeListener(IHandler listener) {
|
||||
if(((inlinedListeners == null) || (!inlinedListeners.remove(listener))) && (threadedListeners != null)) {
|
||||
threadedListeners.remove(listener);
|
||||
}//if//
|
||||
|
||||
//Cleanup the collections.//
|
||||
if((inlinedListeners != null) && (inlinedListeners.getSize() == 0)) {
|
||||
inlinedListeners = null;
|
||||
}//if//
|
||||
|
||||
if((threadedListeners != null) && (threadedListeners.getSize() == 0)) {
|
||||
threadedListeners = null;
|
||||
}//if//
|
||||
}//removeListener()//
|
||||
public int getListenerCount() {
|
||||
return (inlinedListeners != null ? inlinedListeners.getSize() : 0) + (threadedListeners != null ? threadedListeners.getSize() : 0);
|
||||
}//getListenerCount()//
|
||||
public IHashSet getListeners(boolean inlined) {
|
||||
return inlined ? inlinedListeners : threadedListeners;
|
||||
}//getListeners()//
|
||||
}//ListenerSets//
|
||||
/**
|
||||
* Gets the event name for the given event number.
|
||||
* @param type The class that defines (directly or indirectly) the event.
|
||||
* @param eventNumber The unqiue (within the context of the type hierarchy) number of the event.
|
||||
* @return The event name, or null if the event could not be found.
|
||||
*/
|
||||
public static String getEventName(Class type, int eventNumber) {
|
||||
return MetadataService.getSingleton().getEventName(type, eventNumber);
|
||||
}//getEventName()//
|
||||
/**
|
||||
* Gets the event number for the given event name.
|
||||
* @param type The class that defines (directly or indirectly) the event.
|
||||
* @param eventName The unqiue (within the context of the type hierarchy) name of the event.
|
||||
* @return The event number, or -1 if the event could not be found.
|
||||
*/
|
||||
public static int getEventNumber(Class type, String eventName) {
|
||||
return MetadataService.getSingleton().getEventNumber(type, eventName);
|
||||
}//getEventNumber()//
|
||||
/**
|
||||
* Determines whether the event that passed the given event flags is a standard change event.
|
||||
* @param eventFlags The event's flags.
|
||||
* @return Whether the event is considered to be standard. Most event listeners are only interested in standard events that indicate a value has really been changed.
|
||||
*/
|
||||
public static boolean isStandardEvent(int eventFlags) {
|
||||
return eventFlags == 0;
|
||||
}//isStandardEvent()//
|
||||
/**
|
||||
* Determines whether the event that passed the given event flags is a change under a trusted context.
|
||||
* @param eventFlags The event's flags.
|
||||
* @return Whether the change represented by the event is a trusted change, meaning that most listeners should ignore it, but some may need to take action.
|
||||
* @see #FLAG_TRUSTED_CHANGE
|
||||
*/
|
||||
public static boolean isTrustedChangeEvent(int eventFlags) {
|
||||
return (eventFlags & FLAG_TRUSTED_CHANGE) > 0;
|
||||
}//isTrustedChangeEvent()//
|
||||
/**
|
||||
* Determines whether the event that passed the given event flags is a change due to the reflected object updating its reflections.
|
||||
* @param eventFlags The event's flags.
|
||||
* @return Whether the change represented by the event is due to an update from the reflected object.
|
||||
* @see #FLAG_REFLECTION_UPDATE
|
||||
*/
|
||||
public static boolean isReflectionUpdateChangeEvent(int eventFlags) {
|
||||
return (eventFlags & FLAG_REFLECTION_UPDATE) > 0;
|
||||
}//isReflectionUpdateChangeEvent()//
|
||||
/**
|
||||
* Determines whether the event that passed the given event flags is indicating that the original value has been altered.
|
||||
* An event with this flag does not indicate a change in the current value. This is used only when the value has been altered locally and a remote update alters the value that the current value was originally based on.
|
||||
* @param eventFlags The event's flags.
|
||||
* @return Whether the change represented by the event is just changing the original value and not the current value.
|
||||
* @see #FLAG_ORIGINAL_VALUE_CHANGED
|
||||
*/
|
||||
public static boolean isOriginalValueChanged(int eventFlags) {
|
||||
return (eventFlags & FLAG_ORIGINAL_VALUE_CHANGED) > 0;
|
||||
}//isOriginalValueChanged()//
|
||||
/**
|
||||
* Determines whether the event that passed the given event flags is indicating that the original value has been cleared.
|
||||
* An event with this flag does not indicate a change in the current value. This is used only when the value has been altered locally and a remote update alters the value that the current value was originally based on.
|
||||
* @param eventFlags The event's flags.
|
||||
* @return Whether the change represented by the event is just changing the original value and not the current value.
|
||||
* @see #FLAG_ORIGINAL_VALUE_CHANGED
|
||||
*/
|
||||
public static boolean isOriginalValueCleared(int eventFlags) {
|
||||
return (eventFlags & FLAG_ORIGINAL_VALUE_CLEARED) > 0;
|
||||
}//isOriginalValueCleared()//
|
||||
/**
|
||||
* EventSupport constructor.
|
||||
*/
|
||||
protected EventSupport() {
|
||||
super();
|
||||
}//EventSupport()//
|
||||
/**
|
||||
* EventSupport constructor.
|
||||
* @param supportedObject The object that is being supported. This is required if events will be fired. This is optional if the support object is only being used to listen for events fired by other objects.
|
||||
*/
|
||||
public EventSupport(IEventEmitter supportedObject) {
|
||||
super();
|
||||
|
||||
this.supportedObject = supportedObject;
|
||||
}//EventSupport()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
*/
|
||||
public void fireEvent(Event event) {
|
||||
fireEvent(event.getNumber(), new Object[0], false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with an array of parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters The array of all parameters.
|
||||
*/
|
||||
public void fireEvent(Event event, Object[] parameters) {
|
||||
fireEvent(event.getNumber(), parameters, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with an array of parameters.
|
||||
* @param eventName The name of the event to be fired.
|
||||
* @param parameters The array of all parameters.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response). This should almost always be true.
|
||||
* @param isChangeTrusted Whether the change was a trusted change. A trusted change is one that altered data to be what it should have been all along. A lazy load is an example of a trusted change.
|
||||
*/
|
||||
public void fireEvent(Event event, Object[] parameters, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
fireEvent(event.getNumber(), parameters, false, flags);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with one parameter.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with two parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter1, Object parameter2) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with three parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param parameters3 The third parameter.
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter1, Object parameter2, Object parameter3) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2, parameter3}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with three parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param parameters3 The third parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter1, Object parameter2, Object parameter3, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2, parameter3}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with two parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter1, Object parameter2, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter1, parameter2}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with one parameter.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(Event event, Object parameter, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(event.getNumber(), new Object[] {parameter}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(Event event, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(event.getNumber(), new Object[0], makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
* @param flags The event dependant flags.
|
||||
*/
|
||||
public void fireEvent(Event event, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
fireEvent(event.getNumber(), new Object[0], makeAynchronousRemoteCalls, flags);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
*/
|
||||
public void fireEvent(int eventNumber) {
|
||||
fireEvent(eventNumber, new Object[0], false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with an array of parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters The array of all parameters.
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object[] parameters) {
|
||||
fireEvent(eventNumber, parameters, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with an array of parameters.
|
||||
* @param eventName The name of the event to be fired.
|
||||
* @param parameters The array of all parameters.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response). This should almost always be true.
|
||||
* @param flags
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object[] parameters, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
if(hasListeners(eventNumber)) {
|
||||
try {
|
||||
ListenerSets listeners = getListeners(eventNumber);
|
||||
IHashSet inlinedListeners = listeners.getListeners(true);
|
||||
IHashSet threadedListeners = listeners.getListeners(false);
|
||||
|
||||
//Call the inlined listeners with this thread. Note that this is a performance problem if the handler is not fast.//
|
||||
//This is necessary for the view code since it dramatically improves performance since events get processed directly on the event thread (views and associated models are single threaded).//
|
||||
if((inlinedListeners != null) && (inlinedListeners.getSize() > 0)) {
|
||||
Object[] inlinedListenerArray = inlinedListeners.toArray();
|
||||
|
||||
for(int index = 0; index < inlinedListenerArray.length; index++) {
|
||||
IHandler handler = (IHandler) inlinedListenerArray[index];
|
||||
|
||||
if((makeAynchronousRemoteCalls) && (Orb.isProxy(handler))) {
|
||||
Orb.setOneWayCall(handler);
|
||||
}//if//
|
||||
|
||||
if(handler instanceof IEventHandler) {
|
||||
((IEventHandler) handler).evaluate(Orb.isLocal(handler) ? getSupportedObject() : (IEventEmitter) Orb.getProxy(getSupportedObject(), IEventEmitter.class), eventNumber, parameters, flags);
|
||||
}//if//
|
||||
else {
|
||||
handler.evaluate(eventNumber, parameters, flags);
|
||||
}//else//
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
//Call the threaded listeners (most non-gui listeners) using the event notifier which will preserve the ordering of the events and optimize thread useage.//
|
||||
if((threadedListeners != null) && (threadedListeners.getSize() > 0)) {
|
||||
ICollection threadedListenerList = new LiteList((ICollection) threadedListeners);
|
||||
|
||||
EventNotifier.getSingleton().manageEvent(getSupportedObject(), eventNumber, parameters, threadedListenerList, makeAynchronousRemoteCalls, flags);
|
||||
}//if//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
Debug.halt();
|
||||
}//catch//
|
||||
}//if//
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with one parameter.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter) {
|
||||
fireEvent(eventNumber, new Object[] {parameter}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with two parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter1, Object parameter2) {
|
||||
fireEvent(eventNumber, new Object[] {parameter1, parameter2}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with three parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param parameters3 The third parameter.
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, Object parameter3) {
|
||||
fireEvent(eventNumber, new Object[] {parameter1, parameter2, parameter3}, false, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with three parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param parameters3 The third parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, Object parameter3, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(eventNumber, new Object[] {parameter1, parameter2, parameter3}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with two parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param parameters2 The second parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter1, Object parameter2, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(eventNumber, new Object[] {parameter1, parameter2}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with one parameter.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param parameters1 The first parameter.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(int eventNumber, Object parameter, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(eventNumber, new Object[] {parameter}, makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
*/
|
||||
public void fireEvent(int eventNumber, boolean makeAynchronousRemoteCalls) {
|
||||
fireEvent(eventNumber, new Object[0], makeAynchronousRemoteCalls, 0);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Fires an event with no parameters.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be fired.
|
||||
* @param makeAynchronousRemoteCalls Whether remote event handlers should be called asynchronously (in other words don't wait for any response).
|
||||
* @param flags The event dependant flags.
|
||||
*/
|
||||
public void fireEvent(int eventNumber, boolean makeAynchronousRemoteCalls, int flags) {
|
||||
fireEvent(eventNumber, new Object[0], makeAynchronousRemoteCalls, flags);
|
||||
}//fireEvent()//
|
||||
/**
|
||||
* Gets the listeners for a perticular event.
|
||||
* @param eventNumber The unqiue number of the event the listeners are listening to.
|
||||
* @return The collection of inlined and threaded listeners.
|
||||
*/
|
||||
private ListenerSets getListeners(int eventNumber) {
|
||||
return (ListenerSets) threadedListenerMap.get(eventNumber);
|
||||
}//getListeners()//
|
||||
/**
|
||||
* Gets or creates the event hashmap which indexes a single IHandler or a set of IHandlers by the event the handlers process.
|
||||
* @param eventEmitter The emitter whose event map is to be retrieved.
|
||||
* @return The mapping of event handlers by event number.
|
||||
*/
|
||||
private IntObjectHashMap getOrCreateEventMap(IEventEmitter eventEmitter) {
|
||||
IntObjectHashMap result = (IntObjectHashMap) emitterMap.get(eventEmitter);
|
||||
|
||||
if(result == null) {
|
||||
result = new IntObjectHashMap(10);
|
||||
emitterMap.put(eventEmitter, result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getOrCreateEventMap()//
|
||||
/**
|
||||
* Retrieves a collection of listeners listening to a specific event.
|
||||
* <p>Warning: All calling methods must have first synchronized on this.</p>
|
||||
* @param eventName The name of the event the listeners are listening to.
|
||||
* @param inlineListeners Whether the listeners set returned is for the inlined listeners, versus the threaded listeners.
|
||||
* @return The collection of listeners.
|
||||
*/
|
||||
private ListenerSets getOrCreateListeners(int eventNumber) {
|
||||
ListenerSets result = (ListenerSets) threadedListenerMap.get(eventNumber);
|
||||
|
||||
if(result == null) {
|
||||
result = new ListenerSets();
|
||||
threadedListenerMap.put(eventNumber, result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getOrCreateListeners()//
|
||||
/**
|
||||
* Gets the object that we are supporting.
|
||||
* @return The object this object is supporting.
|
||||
*/
|
||||
public IEventEmitter getSupportedObject() {
|
||||
return supportedObject;
|
||||
}//getSupportedObject()//
|
||||
/**
|
||||
* Determines whether there are listeners to a perticular event.
|
||||
* @param event The event.
|
||||
* @return Whether there are any event listeners.
|
||||
*/
|
||||
public boolean hasListeners(Event event) {
|
||||
return hasListeners(event.getNumber());
|
||||
}//hasListeners()//
|
||||
/**
|
||||
* Determines whether there are listeners to a perticular event.
|
||||
* <p>Warning: This method is not synchronized since it is unlikly that making it synchronized is necessary. It is possible however that this thread's local cache of memory will be used instead of accessing the shared memory for the hashmap value.</p>
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event.
|
||||
* @return Whether there are any event listeners.
|
||||
*/
|
||||
public boolean hasListeners(int eventNumber) {
|
||||
return threadedListenerMap.get(eventNumber) != null;
|
||||
}//hasListeners()//
|
||||
/**
|
||||
* Reads the event emitter support from a stream.
|
||||
* @param in The input stream containing the support data.
|
||||
*/
|
||||
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, ClassNotFoundException {
|
||||
Class type = (Class) in.readObject();
|
||||
int eventCount = in.readInt();
|
||||
|
||||
threadedListenerMap = new IntObjectHashMap(20);
|
||||
|
||||
while(eventCount-- > 0) {
|
||||
String eventName = in.readUTF();
|
||||
int eventNumber = getEventNumber(type, eventName);
|
||||
int listenerCount = in.readInt();
|
||||
ListenerSets listeners = getOrCreateListeners(eventNumber);
|
||||
|
||||
//Read the inlined listeners.//
|
||||
while(listenerCount-- > 0) {
|
||||
IEventHandler eventHandler = (IEventHandler) in.readObject();
|
||||
|
||||
//Check just in case there was an error serializing.//
|
||||
if(eventHandler != null) {
|
||||
listeners.addListener(eventHandler, true);
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
//Read the threaded listeners.//
|
||||
listenerCount = in.readInt();
|
||||
|
||||
while(listenerCount-- > 0) {
|
||||
IEventHandler eventHandler = (IEventHandler) in.readObject();
|
||||
|
||||
//Check just in case there was an error serializing.//
|
||||
if(eventHandler != null) {
|
||||
listeners.addListener(eventHandler, false);
|
||||
}//if//
|
||||
}//while//
|
||||
}//while//
|
||||
}//readExternal()//
|
||||
/**
|
||||
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
|
||||
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
|
||||
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
|
||||
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
|
||||
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param event The event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, Event event, IEventHandler handler) {
|
||||
register(eventEmitter, event.getNumber(), (IHandler) handler, false);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
|
||||
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
|
||||
* <p>This method should not usually be used directly.
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param event The event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @see #register(IEventEmitter, int, Handler)
|
||||
* @see #register(IEventEmitter, int, IEventHandler)
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, Event event, IHandler handler) {
|
||||
register(eventEmitter, event.getNumber(), (IHandler) handler, false);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter.
|
||||
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
|
||||
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
|
||||
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
|
||||
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param event The event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, Event event, IEventHandler handler, boolean inline) {
|
||||
register(eventEmitter, event.getNumber(), (IHandler) handler, inline);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter.
|
||||
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
|
||||
* <p>This method should not usually be used directly.
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param event The event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
|
||||
* @see #register(IEventEmitter, String, Handler, boolean)
|
||||
* @see #register(IEventEmitter, String, IEventHandler, boolean)
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, Event event, IHandler handler, boolean inline) {
|
||||
register(eventEmitter, event.getNumber(), handler, inline);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
|
||||
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
|
||||
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
|
||||
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
|
||||
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, int eventNumber, IEventHandler handler) {
|
||||
register(eventEmitter, eventNumber, (IHandler) handler, false);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter to be handled in a separate event thread (not inlined).
|
||||
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
|
||||
* <p>This method should not usually be used directly.
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @see #register(IEventEmitter, int, Handler)
|
||||
* @see #register(IEventEmitter, int, IEventHandler)
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, int eventNumber, IHandler handler) {
|
||||
register(eventEmitter, eventNumber, (IHandler) handler, false);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter.
|
||||
* This method takes an IEventHandler that behaves just like a Handler except that it gets passed the event emitter reference when invoked.
|
||||
* <p>Note: If the emitter is remote then the event emitter reference passed to the handler when the event is fired will be a proxy to the original event emitter.
|
||||
* This reference is intended only to allow the handler to be registered with multiple emitters and to be able to distinguish between them when receiving events.
|
||||
* <b>It is not guanenteed that the passed emitter reference will be useable for anything other than comparison with another proxy to the same emitter.</b>
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register with.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
|
||||
*/
|
||||
public void register(IEventEmitter eventEmitter, int eventNumber, IEventHandler handler, boolean inline) {
|
||||
register(eventEmitter, eventNumber, (IHandler) handler, inline);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener with an event emitter.
|
||||
* A handler can be registered multiple times, as long as it is not registered twice to the same combination of event emitter and event name.
|
||||
* <p>This method should not usually be used directly.
|
||||
* @param eventEmitter The event emitter that will fire the event that the handler will handle.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to register.
|
||||
* @param handler The handler to invoke when the event is fired by the event emitter.
|
||||
* @param inline Whether the event handler should be called on the thread firing the event at the time the event is fired, versus being called by the event processing thread in the order the event occured. This should normally be false, except for GUI registrants which generally have one thread for all GUI and related model access and can avoid thread context switching by inlining.
|
||||
* @see #register(IEventEmitter, String, Handler, boolean)
|
||||
* @see #register(IEventEmitter, String, IEventHandler, boolean)
|
||||
*/
|
||||
public synchronized void register(IEventEmitter eventEmitter, int eventNumber, IHandler handler, boolean inline) {
|
||||
IntObjectHashMap eventNameMap = getOrCreateEventMap(eventEmitter);
|
||||
Object value = eventNameMap.get(eventNumber);
|
||||
|
||||
if(value == null) {
|
||||
eventNameMap.put(eventNumber, handler);
|
||||
}//if//
|
||||
else if(value instanceof IHashSet) {
|
||||
if(((IHashSet) value).add(handler) == -1) {
|
||||
throw new IllegalArgumentException("Cannot register the same handler for the same event on the same event emitter. At least one of the three variables must be different.");
|
||||
}//if//
|
||||
}//else if//
|
||||
else {
|
||||
IHashSet set = new LiteHashSet(10);
|
||||
|
||||
set.add(value);
|
||||
set.add(handler);
|
||||
eventNameMap.put(eventNumber, set);
|
||||
}//else//
|
||||
|
||||
eventEmitter.registerListener(eventNumber, Orb.isLocal(eventEmitter) ? handler : (IHandler) (handler instanceof IEventHandler ? Orb.getProxy(handler, IEventHandler.class) : Orb.getProxy(handler, IHandler.class)), inline);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a listener to a perticular event.
|
||||
* <p>Warning: This method is to be called by the event emitter. Use the register(..) methods if using the event support to register with an event emitter.</p>
|
||||
* @param event The event to be listened to.
|
||||
* @param eventHandler The handler to be called if the event is fired.
|
||||
* @param inlined Whether the handler should be called using the same thread that fired the event, versus being called on the event thread in the order it was fired. For performance reasons this should generally be false.
|
||||
*/
|
||||
public void registerListener(Event event, IHandler eventHandler, boolean inlined) {
|
||||
registerListener(event.getNumber(), eventHandler, inlined);
|
||||
}//registerListener()//
|
||||
/**
|
||||
* Registers a listener to a perticular event.
|
||||
* <p>Warning: This method is to be called by the event emitter. Use the register(..) methods if using the event support to register with an event emitter.</p>
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be listened to.
|
||||
* @param eventHandler The handler to be called if the event is fired.
|
||||
* @param inlined Whether the handler should be called using the same thread that fired the event, versus being called on the event thread in the order it was fired. For performance reasons this should generally be false.
|
||||
*/
|
||||
public synchronized void registerListener(int eventNumber, IHandler eventHandler, boolean inlined) {
|
||||
ListenerSets listeners = getOrCreateListeners(eventNumber);
|
||||
|
||||
if(eventHandler == null) {
|
||||
throw new NullPointerException("Null event handler passed when registering for an event with this event emitter.");
|
||||
}//if//
|
||||
|
||||
listeners.addListener(eventHandler, inlined);
|
||||
}//registerListener()//
|
||||
/**
|
||||
* Unregisters the specific event handler so it does not receive events from the given event emitter for the given event name.
|
||||
* @param eventEmitter The event emitter to unregister with.
|
||||
* @param event The event to be unregistered.
|
||||
* @param eventHandler The specific handler to be unregistered.
|
||||
*/
|
||||
public void unregister(IEventEmitter eventEmitter, Event event, IHandler eventHandler) {
|
||||
unregister(eventEmitter, event.getNumber(), eventHandler);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters the specific event handler so it does not receive events from the given event emitter for the given event name.
|
||||
* @param eventEmitter The event emitter to unregister with.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event to be unregistered.
|
||||
* @param eventHandler The specific handler to be unregistered.
|
||||
*/
|
||||
public synchronized void unregister(IEventEmitter eventEmitter, int eventNumber, IHandler eventHandler) {
|
||||
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
|
||||
|
||||
if(eventMap != null) {
|
||||
Object value = eventMap.get(eventNumber);
|
||||
|
||||
if(value instanceof IHashSet) {
|
||||
((IHashSet) value).remove(eventHandler);
|
||||
}//if//
|
||||
else {
|
||||
eventMap.remove(eventNumber);
|
||||
}//else//
|
||||
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, eventHandler);
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters all registered event handlers with all event emitters.
|
||||
*/
|
||||
public synchronized void unregisterAll() {
|
||||
IIterator emitterIterator = emitterMap.keyIterator();
|
||||
|
||||
while(emitterIterator.hasNext()) {
|
||||
IEventEmitter eventEmitter = (IEventEmitter) emitterIterator.next();
|
||||
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
|
||||
|
||||
if(eventMap != null) {
|
||||
IIntIterator eventIterator = eventMap.keyIterator();
|
||||
|
||||
while(eventIterator.hasNext()) {
|
||||
int eventNumber = eventIterator.next();
|
||||
Object value = eventMap.get(eventNumber);
|
||||
|
||||
if(value instanceof IHashSet) {
|
||||
IIterator handlerIterator = ((IHashSet) value).iterator();
|
||||
|
||||
while(handlerIterator.hasNext()) {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
|
||||
}//while//
|
||||
|
||||
((IHashSet) value).removeAll(); //Help GC.//
|
||||
}//if//
|
||||
else {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
|
||||
}//else//
|
||||
}//while//
|
||||
|
||||
eventMap.removeAll(); //Help GC.//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
emitterMap.removeAll(); //Help GC.//
|
||||
}//unregisterAll()//
|
||||
/**
|
||||
* Unregisters all registered event handlers for a single event emitter.
|
||||
* @param eventEmitter The event emitter for which all registered events will be unregistered.
|
||||
*/
|
||||
public synchronized void unregisterAll(IEventEmitter eventEmitter) {
|
||||
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.remove(eventEmitter);
|
||||
|
||||
if(eventMap != null) {
|
||||
IIntIterator eventIterator = eventMap.keyIterator();
|
||||
|
||||
while(eventIterator.hasNext()) {
|
||||
int eventNumber = eventIterator.next();
|
||||
Object value = eventMap.get(eventNumber);
|
||||
|
||||
if(value instanceof IHashSet) {
|
||||
IIterator handlerIterator = ((IHashSet) value).iterator();
|
||||
|
||||
while(handlerIterator.hasNext()) {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
|
||||
}//while//
|
||||
|
||||
((IHashSet) value).removeAll(); //Help GC.//
|
||||
}//if//
|
||||
else {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
|
||||
}//else//
|
||||
}//while//
|
||||
|
||||
eventMap.removeAll(); //Help GC.//
|
||||
}//if//
|
||||
}//unregisterAll()//
|
||||
/**
|
||||
* Unregisters all registered event handlers for a single event emitter and a single event name.
|
||||
* @param eventEmitter The event emitter for which all registered events will be unregistered.
|
||||
* @param event The event for which all registered events will be unregistered.
|
||||
*/
|
||||
public void unregisterAll(IEventEmitter eventEmitter, Event event) {
|
||||
unregisterAll(eventEmitter, event.getNumber());
|
||||
}//unregisterAll()//
|
||||
/**
|
||||
* Unregisters all registered event handlers for a single event emitter and a single event name.
|
||||
* @param eventEmitter The event emitter for which all registered events will be unregistered.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event for which all registered events will be unregistered.
|
||||
*/
|
||||
public synchronized void unregisterAll(IEventEmitter eventEmitter, int eventNumber) {
|
||||
IntObjectHashMap eventMap = (IntObjectHashMap) emitterMap.get(eventEmitter);
|
||||
|
||||
if(eventMap != null) {
|
||||
Object value = eventMap.remove(eventNumber);
|
||||
|
||||
if(value instanceof IHashSet) {
|
||||
IIterator handlerIterator = ((IHashSet) value).iterator();
|
||||
|
||||
while(handlerIterator.hasNext()) {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) handlerIterator.next());
|
||||
}//while//
|
||||
}//if//
|
||||
else {
|
||||
if(!Orb.isLocal(eventEmitter)) {
|
||||
Orb.setOneWayCall(eventEmitter);
|
||||
}//if//
|
||||
|
||||
eventEmitter.unregisterListener(eventNumber, (IHandler) value);
|
||||
}//else//
|
||||
|
||||
if(eventMap.getSize() == 0) {
|
||||
emitterMap.remove(eventEmitter);
|
||||
}//if//
|
||||
}//if//
|
||||
}//unregisterAll()//
|
||||
/**
|
||||
* Allows the IEventListener object to unregister it's event handlers.
|
||||
* <p>Warning: This method is to be called by the event emitter. Use the unregister(..) methods if using the event support to unregister with an event emitter.</p>
|
||||
* @param event The event whose listener will be unregistered.
|
||||
* @param eventHandler The event handler that had been receiving event notifications.
|
||||
*/
|
||||
public void unregisterListener(Event event, IHandler eventHandler) {
|
||||
unregisterListener(event.getNumber(), eventHandler);
|
||||
}//unregisterListener()//
|
||||
/**
|
||||
* Allows the IEventListener object to unregister it's event handlers.
|
||||
* <p>Warning: This method is to be called by the event emitter. Use the unregister(..) methods if using the event support to unregister with an event emitter.</p>
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event whose listener will be unregistered.
|
||||
* @param eventHandler The event handler that had been receiving event notifications.
|
||||
*/
|
||||
public synchronized void unregisterListener(int eventNumber, IHandler eventHandler) {
|
||||
ListenerSets listeners = getOrCreateListeners(eventNumber);
|
||||
|
||||
listeners.removeListener(eventHandler);
|
||||
|
||||
if(listeners.getListenerCount() == 0) {
|
||||
threadedListenerMap.remove(eventNumber);
|
||||
}//if//
|
||||
}//unregisterListener()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public synchronized void writeExternal(java.io.ObjectOutput out) throws java.io.IOException {
|
||||
Class type = supportedObject.getClass();
|
||||
|
||||
out.writeObject(type); //Note: The supported object class is required to convert the event names back to numbers.//
|
||||
out.writeInt(threadedListenerMap.getSize());
|
||||
IIntIterator iterator = threadedListenerMap.keyIterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
int eventNumber = iterator.next();
|
||||
ListenerSets listeners = (ListenerSets) threadedListenerMap.get(eventNumber);
|
||||
IHashSet inlinedListeners = listeners.getListeners(true);
|
||||
IHashSet threadedListeners = listeners.getListeners(false);
|
||||
IIterator listenerIterator = inlinedListeners.iterator();
|
||||
|
||||
out.writeUTF(getEventName(type, eventNumber));
|
||||
//Write out the inlined listeners.//
|
||||
out.writeInt(inlinedListeners.getSize());
|
||||
|
||||
while(listenerIterator.hasNext()) {
|
||||
IEventHandler listener = (IEventHandler) listenerIterator.next();
|
||||
|
||||
try {
|
||||
out.writeObject(Orb.getProxy(listener, IEventHandler.class));
|
||||
}//try//
|
||||
catch(IllegalArgumentException e) {
|
||||
Debug.log(e);
|
||||
out.writeObject(null);
|
||||
}//catch//
|
||||
}//while//
|
||||
|
||||
//Write out the threaded listeners.//
|
||||
out.writeInt(threadedListeners.getSize());
|
||||
listenerIterator = threadedListeners.iterator();
|
||||
|
||||
while(listenerIterator.hasNext()) {
|
||||
IEventHandler listener = (IEventHandler) listenerIterator.next();
|
||||
|
||||
try {
|
||||
out.writeObject(Orb.getProxy(listener, IEventHandler.class));
|
||||
}//try//
|
||||
catch(IllegalArgumentException e) {
|
||||
Debug.log(e);
|
||||
out.writeObject(null);
|
||||
}//catch//
|
||||
}//while//
|
||||
}//while//
|
||||
}//writeExternal()//
|
||||
}//EventEmitterSupport//
|
||||
43
Foundation/src/com/foundation/event/IEventEmitter.java
Normal file
43
Foundation/src/com/foundation/event/IEventEmitter.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
import com.foundation.metadata.Event;
|
||||
|
||||
public interface IEventEmitter {
|
||||
/**
|
||||
* Registers a listener with this event emitter for notification when an event is fired.
|
||||
* @param eventNumber The number of the event to register with. Event numbers are either the attribute or custom event identifier defined by the declaring type.
|
||||
* @param handler The handler that will be evaluated when ever the event is fired.
|
||||
* @param inline Whether the handler call should be inlined such that the firing thread notifies the handler, versus an event processing thread notifying the handler in the order the event occured.
|
||||
* @see #unregisterListener(int, IHandler)
|
||||
*/
|
||||
public void registerListener(int eventNumber, IHandler handler, boolean inline);
|
||||
/**
|
||||
* Unregisters a listener from this event emitter.
|
||||
* @param eventNumber The number of the event to unregister from. Event numbers are either the attribute or custom event identifier defined by the declaring type.
|
||||
* @param handler The handler that was orgininally registered.
|
||||
* @see #registerListener(int, IHandler, boolean)
|
||||
*/
|
||||
public void unregisterListener(int eventNumber, IHandler handler);
|
||||
/**
|
||||
* Registers a listener with this event emitter for notification when an event is fired.
|
||||
* @param eventNumber The number of the event to register with. Event numbers are either the attribute or custom event identifier defined by the declaring type.
|
||||
* @param handler The handler that will be evaluated when ever the event is fired.
|
||||
* @param inline Whether the handler call should be inlined such that the firing thread notifies the handler, versus an event processing thread notifying the handler in the order the event occured.
|
||||
* @see #unregisterListener(int, IHandler)
|
||||
*/
|
||||
public void registerListener(Event eventNumber, IHandler handler, boolean inline);
|
||||
/**
|
||||
* Unregisters a listener from this event emitter.
|
||||
* @param eventNumber The number of the event to unregister from. Event numbers are either the attribute or custom event identifier defined by the declaring type.
|
||||
* @param handler The handler that was orgininally registered.
|
||||
* @see #registerListener(int, IHandler, boolean)
|
||||
*/
|
||||
public void unregisterListener(Event eventNumber, IHandler handler);
|
||||
}//IEventEmitter//
|
||||
23
Foundation/src/com/foundation/event/IEventHandler.java
Normal file
23
Foundation/src/com/foundation/event/IEventHandler.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
/**
|
||||
* This interface defines a basic event handler that will allow an event handler to be passed remotely.
|
||||
* <p>NOTE: This interface should never be used directly by an application.</p>
|
||||
*/
|
||||
public interface IEventHandler extends IHandler {
|
||||
/**
|
||||
* Does what ever the handler is programmed to do when called.
|
||||
* @param eventEmitter The object that fired the event.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event being fired. This allows one handler to register for multiple events.
|
||||
* @param parameters The parameters passed to the handler when it is invoked.
|
||||
* @param flags The event dependant flags.
|
||||
*/
|
||||
public abstract void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags);
|
||||
}//IEventHandler//
|
||||
11
Foundation/src/com/foundation/event/IEventListener.java
Normal file
11
Foundation/src/com/foundation/event/IEventListener.java
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
public interface IEventListener {
|
||||
}//IEventListener//
|
||||
22
Foundation/src/com/foundation/event/IHandler.java
Normal file
22
Foundation/src/com/foundation/event/IHandler.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.event;
|
||||
|
||||
/**
|
||||
* This interface defines a basic event handler that will allow an event handler to be passed remotely.
|
||||
* <p>NOTE: This interface should never be used directly by an application.</p>
|
||||
*/
|
||||
public interface IHandler {
|
||||
/**
|
||||
* Does what ever the handler is programmed to do when called.
|
||||
* @param eventNumber The unique (within the context of the defining class hierarchy) number of the event being fired. This allows one handler to register for multiple events.
|
||||
* @param parameters The parameters passed to the handler when it is invoked.
|
||||
* @param flags A set of flags that depend on the event type. These flags are event dependant.
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags);
|
||||
}//IHandler//
|
||||
45
Foundation/src/com/foundation/event/IRequestHandler.java
Normal file
45
Foundation/src/com/foundation/event/IRequestHandler.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2008 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.event;
|
||||
|
||||
import com.common.thread.*;
|
||||
|
||||
/**
|
||||
* Defines the methods of a request handler which processes requests in sequential order on a single request thread.
|
||||
*/
|
||||
public interface IRequestHandler {
|
||||
/**
|
||||
* Determines whether the calling thread is the thread that handles requests.
|
||||
* @return Whether the calling thread is the request thread.
|
||||
*/
|
||||
public boolean isRequestThread();
|
||||
/**
|
||||
* Executes a runnable through the request thread. This method allows single threading of all requests.
|
||||
* @param runnable The runnable to be executed.
|
||||
* @return The result of the runnable.
|
||||
*/
|
||||
public Object execute(IRunnable runnable);
|
||||
/**
|
||||
* Executes a runnable through the request thread without waiting for the response. This method allows single threading of all requests.
|
||||
* If the calling thread is the request thread, the runnable will not be run immediatly, instead it will be run at some point in the future when this thread finishes its current task and releases control.
|
||||
* @param runnable The runnable to be executed.
|
||||
*/
|
||||
public void executeAsync(IRunnable runnable);
|
||||
/**
|
||||
* Executes a runnable through the event thread. This method allows single threading of an entire view.
|
||||
* @param runnable The runnable to be executed.
|
||||
* @param synchronous Whether the thread should block until the request has been processed.
|
||||
* @return The result of the runnable, or null if asynchronous.
|
||||
*/
|
||||
public Object execute(IRunnable runnable, boolean synchronous);
|
||||
/**
|
||||
* Determines whether the event loop is activly running.
|
||||
* @return Whether the loop is active.
|
||||
*/
|
||||
public boolean isRunning();
|
||||
}//IRequestHandler//
|
||||
57
Foundation/src/com/foundation/event/ListenerSet.java
Normal file
57
Foundation/src/com/foundation/event/ListenerSet.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2007 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.event;
|
||||
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
|
||||
/*
|
||||
* Extends the LiteHashSet to provide efficient retreival of a set of listeners conforming to a specific class.
|
||||
*/
|
||||
public class ListenerSet extends LiteHashSet {
|
||||
public static class ListenerSetIterator extends LiteHashSet.HashSetIterator {
|
||||
private Class listenerClass = null;
|
||||
|
||||
/**
|
||||
* ListenerSetIterator constructor.
|
||||
* @param hashSet The set of all listeners.
|
||||
* @param listenerClass The the class that the resulting listeners must implement/extend.
|
||||
*/
|
||||
protected ListenerSetIterator(LiteHashSet hashSet, Class listenerClass) {
|
||||
super(hashSet);
|
||||
this.listenerClass = listenerClass;
|
||||
}//ListenerSetIterator()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.util.LiteHashSet.HashSetIterator#loadNextEntry()
|
||||
*/
|
||||
protected void loadNextEntry() {
|
||||
super.loadNextEntry();
|
||||
|
||||
//Skip next values which don't conform.//
|
||||
while(peekHasNext() && (!(listenerClass.isAssignableFrom(peekNext().getClass())))) {
|
||||
currentEntry = nextEntry;
|
||||
currentIndex = nextIndex;
|
||||
hasNextEntry = false;
|
||||
nextEntry = null;
|
||||
}//while//
|
||||
}//loadNextEntry()//
|
||||
}//ListenerSetIterator//
|
||||
/**
|
||||
* ListenerSet constructor.
|
||||
* @param initialCapacity
|
||||
*/
|
||||
public ListenerSet(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}//ListenerSet()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.util.LiteHashSet#iterator()
|
||||
*/
|
||||
public IIterator iterator(Class listenerClass) {
|
||||
return new ListenerSetIterator(this, listenerClass);
|
||||
}//iterator()//
|
||||
}//ListenerSet//
|
||||
405
Foundation/src/com/foundation/event/model/Binding.java
Normal file
405
Foundation/src/com/foundation/event/model/Binding.java
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.*;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.event.*;
|
||||
|
||||
public abstract class Binding {
|
||||
public static final int FLAG_EXCLUDE_SUBCLASSES = 0x01;
|
||||
|
||||
/** The listener the binding is associated with. */
|
||||
private ModelListener listener = null;
|
||||
/** The support object that can be used to register for events. */
|
||||
private EventSupport eventSupport = null;
|
||||
/** The type of entity which this binding applies to. */
|
||||
private Class entityType = null;
|
||||
/** The flags assigned to this binding. */
|
||||
private int flags = 0;
|
||||
/** A set of metadata objects. Existing metadata for an object can be found by creating a new Metadata with the object and Binding references and then performing a get on this collection passing the new Metadata. Metadata is matched via the object and the source binding using logical equality. */
|
||||
private LiteHashSet metadataSet = new LiteHashSet(10, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getLogicalComparator(), LiteHashSet.STYLE_NO_DUPLICATES);
|
||||
|
||||
/**
|
||||
* Extends the weak reference to provide access to the binding that is using the weak reference.
|
||||
*/
|
||||
protected static class ObjectReference extends WeakReference {
|
||||
/** The binding metadata that is using the reference. This is required to allow the listener to release the binding when the weak reference is invalidated. */
|
||||
private Metadata metadata = null;
|
||||
|
||||
/**
|
||||
* ObjectReference constructor.
|
||||
* @param object The object being referenced weakly.
|
||||
* @param metadata The binding metadata that is using the reference. This is required to allow the listener to release the binding when the weak reference is invalidated.
|
||||
* @param queue The reference queue used to determine when the reference is no longer valid.
|
||||
*/
|
||||
public ObjectReference(Object object, Metadata metadata, ReferenceQueue queue) {
|
||||
super(object, queue);
|
||||
this.metadata = metadata;
|
||||
}//ObjectReference()//
|
||||
/**
|
||||
* Gets the binding metadata that is using the reference.
|
||||
* @return The binding metdata that will be useable to cleanup when the reference is invalidated.
|
||||
*/
|
||||
public Metadata getMetadata() {
|
||||
return metadata;
|
||||
}//getMetadata()//
|
||||
}//ObjectReference//
|
||||
|
||||
/**
|
||||
* Metadata about the registered object in this binding.
|
||||
* <p>This object can be compared to either another Metadata or to the object its metadata describes and the comparison will be deemed equal if the objects are exactly equal.</p>
|
||||
*/
|
||||
protected static abstract class Metadata {
|
||||
/** The weak reference to the object the metadata describes. */
|
||||
private ObjectReference objectRef = null;
|
||||
/** The strong reference to the object the metadata describes. If this is null then the weak reference must not be null. */
|
||||
private Object object;
|
||||
/** Counts the number of times the entity has been registered with this binding. */
|
||||
private int counter = 0;
|
||||
/** A reference to the binding that created this entity metadata. */
|
||||
private Binding binding;
|
||||
/** The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener, or if there are multiple sources. */
|
||||
private Metadata source;
|
||||
/** The hash code for this metadata object. */
|
||||
private int hashCode = 0;
|
||||
|
||||
/**
|
||||
* Metadata constructor.
|
||||
* @param binding The binding that created this metadata.
|
||||
* @param source The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener.
|
||||
* @param object The object the metadata describes.
|
||||
*/
|
||||
public Metadata(Binding binding, Metadata source, Object object) {
|
||||
if(binding == null) {
|
||||
throw new IllegalArgumentException("Must provide a non-null binding.");
|
||||
}//if//
|
||||
|
||||
if(object == null) {
|
||||
throw new IllegalArgumentException("Must provide a non-null object.");
|
||||
}//if//
|
||||
|
||||
this.binding = binding;
|
||||
this.source = source;
|
||||
this.object = object;
|
||||
this.hashCode = object.hashCode() ^ (source == null ? 0 : source.hashCode());
|
||||
}//Metadata()//
|
||||
/**
|
||||
* Converts the metadata's object reference into a weak reference to allow the VM to GC the referenced object when memory is tight.
|
||||
* @param referenceQueue The queue used to release the binding if the object is moved out of memory.
|
||||
*/
|
||||
protected void makeWeak(ReferenceQueue referenceQueue) {
|
||||
this.objectRef = new ObjectReference(object, this, referenceQueue);
|
||||
this.object = null;
|
||||
}//makeWeak()//
|
||||
/**
|
||||
* Gets the single source EntityMetadata for this entity.
|
||||
* The value may be null if the entity is a root, but it may also be null if the entity is no longer referenced.
|
||||
* @return The source EntityMetadata for the entity, or null if the entity is a root, or null if there are multiple sources for the same entity (in which case getSources returns a non-null value).
|
||||
*/
|
||||
protected Metadata getSource() {
|
||||
return source;
|
||||
}//getSource()//
|
||||
/**
|
||||
* Determines whether collection notification is required for the given collection depth level.
|
||||
* @param collectionDepth The level of depth of the collection. A zero depth collection is one that is not contained in another collection.
|
||||
* @param listeners The set of listeners that are interested in the collection change.
|
||||
*/
|
||||
protected IModelListenerCollectionHandler getCollectionModelListener(int collectionDepth) {
|
||||
return null;
|
||||
}//getCollectionModelListener()//
|
||||
/**
|
||||
* Searches for the nearest binding metadata whose object matches the given type.
|
||||
* @param type The type of object being searched for. This can be null in which case the all objects will match and the first will be returned.
|
||||
* @param allowSubclasses Whether to allow subclasses of the type.
|
||||
* @return The first matching object, or null.
|
||||
*/
|
||||
public Object getObject(Class type, boolean allowSubclasses) {
|
||||
Object result = (type == null) || (allowSubclasses && type.isAssignableFrom(getObject().getClass()) || (type.equals(getObject().getClass()))) ? getObject() : null;
|
||||
|
||||
return result == null ? (getSource() != null ? getSource().getObject(type, allowSubclasses) : null) : result;
|
||||
}//getObject()//
|
||||
/**
|
||||
* Gets the binding for this entity metadata.
|
||||
* @return The metadata's binding which created this metadata.
|
||||
*/
|
||||
public Binding getBinding() {
|
||||
return binding;
|
||||
}//getBinding()//
|
||||
/**
|
||||
* Gets the object that this metadata describes.
|
||||
* @return The object wrappered by this metadata.
|
||||
*/
|
||||
public Object getObject() {
|
||||
return object == null ? objectRef.get() : object;
|
||||
}//getObject()//
|
||||
/**
|
||||
* Gets the number of times the entity is referenced by the visible model.
|
||||
* @return The count of references to the entity.
|
||||
*/
|
||||
private int getCounter() {
|
||||
return counter;
|
||||
}//getCounter()//
|
||||
/**
|
||||
* Sets the number of times the entity is referenced by the visible model.
|
||||
* @param counter The count of references to the entity.
|
||||
*/
|
||||
private void setCounter(int counter) {
|
||||
this.counter = counter;
|
||||
}//setCounter()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public final boolean equals(Object metadata) {
|
||||
Object object = getObject();
|
||||
|
||||
return metadata instanceof Metadata ? object == null ? metadata == this : (object == ((Metadata) metadata).getObject() && getSource() == ((Metadata) metadata).getSource()) : false;
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public final int hashCode() {
|
||||
return hashCode;
|
||||
}//hashCode()//
|
||||
/**
|
||||
* Some simple cleanup code that helps the GC thread.
|
||||
*/
|
||||
protected void release() {
|
||||
binding = null;
|
||||
object = null;
|
||||
objectRef = null;
|
||||
source = null;
|
||||
}//release()//
|
||||
}//Metadata//
|
||||
/**
|
||||
* Binding constructor.
|
||||
* @param listener The listener that this binding is associated with.
|
||||
* @param entityType The type of entity that this binding applies to.
|
||||
* @param flags The applicable flags for this binding, or zero.
|
||||
*/
|
||||
public Binding(Class entityType, int flags) {
|
||||
this.entityType = entityType;
|
||||
this.flags = flags;
|
||||
}//Binding()//
|
||||
/**
|
||||
* Gets the model listener this binding is attached to.
|
||||
* @return The attached model listener.
|
||||
*/
|
||||
public ModelListener getListener() {
|
||||
return listener;
|
||||
}//getListener()//
|
||||
/**
|
||||
* Sets the listener this binding is being used by.
|
||||
* @param listener The binding's listener.
|
||||
*/
|
||||
void setListener(ModelListener listener) {
|
||||
if(this.listener == null) {
|
||||
this.listener = listener;
|
||||
this.eventSupport = listener.getEventSupport();
|
||||
}//if//
|
||||
else {
|
||||
//Can't associate the binding with more than one listener.//
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
}//setListener()//
|
||||
/**
|
||||
* Gets the optional handlers called when the attribute value is a collection whose values are changed.
|
||||
* @return Array of collection changed handlers used to notify if the attribute's value is a collection (or collection of collections, etc). The depth of the collection that changed is the index into this array.
|
||||
*/
|
||||
protected IModelListenerCollectionHandler[] getCollectionHandlers() {
|
||||
return null;
|
||||
}//getCollectionHandlers()//
|
||||
/**
|
||||
* Gets the class of entity that this binding applies to.
|
||||
* @return The class of entity the binding applies to.
|
||||
*/
|
||||
public Class getEntityType() {
|
||||
return entityType;
|
||||
}//getEntityType()//
|
||||
/**
|
||||
* Gets the event support usable by this binding to register for events.
|
||||
* @return The support object usable for event registration.
|
||||
*/
|
||||
protected EventSupport getEventSupport() {
|
||||
return eventSupport;
|
||||
}//getEventSupport()//
|
||||
/**
|
||||
* Gets the flags for the binding.
|
||||
* @return The binding's flags.
|
||||
*/
|
||||
protected int getFlags() {
|
||||
return flags;
|
||||
}//getFlags()//
|
||||
/**
|
||||
* Determines whether this binding should not include subclasses of the entity type.
|
||||
* @return Whether subclasses of the entity type don't match this binding.
|
||||
*/
|
||||
public boolean excludeSubclasses() {
|
||||
return (flags & FLAG_EXCLUDE_SUBCLASSES) > 0;
|
||||
}//excludeSubclasses()//
|
||||
/**
|
||||
* Determines whether this binding applies to the given entity.
|
||||
* <p>Subclasses should most likely not override this method.</p>
|
||||
* @param entity The entity that may match this binding.
|
||||
* @param isFirstMatch Whether there was a previous match.
|
||||
* @return Whether this binding can service the entity.
|
||||
*/
|
||||
public boolean matches(IEntity entity, boolean isFirstMatch) {
|
||||
return excludeSubclasses() ? getEntityType().equals(entity.getClass()) : getEntityType().isAssignableFrom(entity.getClass());
|
||||
}//matches()//
|
||||
/**
|
||||
* Gets the existing metadata for the given query metadata.
|
||||
* @param query The metadata used to find the existing metadata. This should have assigned the source and value.
|
||||
* @return The existing metadata or null if it doesn't exist.
|
||||
*/
|
||||
protected Metadata getMetadata(Metadata query) {
|
||||
return (Metadata) metadataSet.get(query);
|
||||
}//getMetadata()//
|
||||
/**
|
||||
* Gets the metadata for the given value and source.
|
||||
* @param value The value that the metadata represents.
|
||||
* @param source The source indicating where the value came from (which binding created it), or null if it is a root value.
|
||||
* @return The metadata for the value and source, or null if the binding doesn't know of the value.
|
||||
*/
|
||||
protected abstract Metadata getMetadata(Object value, Metadata source);
|
||||
/**
|
||||
* Registers the entity with this binding.
|
||||
* @param source The EntityMetadata that generated the entity reference, or null if the entity is root to this listener.
|
||||
* @param entity The entity being registered.
|
||||
* @param handler The optional collection change handler that if provided will be notified when collection changes occur.
|
||||
* @param referenceQueue The reference queue used to cleanup after bindings to objects no longer in memory.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
* @return The metadata that was created as a result of the register. This will be null if the register simply incremented the reference count.
|
||||
*/
|
||||
public Metadata register(Metadata source, Object object, ReferenceQueue referenceQueue, int flags) {
|
||||
Metadata metadata = internalCreateMetadata(source, object);
|
||||
|
||||
//If the metadata already exists (will only be true for collections since they could reference the same object twice) then just increment the counter.//
|
||||
if(metadataSet.containsValue(metadata)) {
|
||||
metadata = (Metadata) metadataSet.get(metadata);
|
||||
metadata.setCounter(metadata.getCounter() + 1);
|
||||
metadata = null;
|
||||
}//if//
|
||||
else {
|
||||
metadata.makeWeak(referenceQueue);
|
||||
metadata.setCounter(metadata.getCounter() + 1);
|
||||
metadataSet.add(metadata);
|
||||
}//else//
|
||||
|
||||
return metadata;
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters the entity with this binding.
|
||||
* @param source The EntityMetadata that generated the entity reference, or null if the entity is root to this listener.
|
||||
* @param entity The entity being unregistered.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
public void unregister(Metadata source, Object object, int flags) {
|
||||
Metadata metadata = internalCreateMetadata(source, object);
|
||||
|
||||
//If the metadata exists in our set then get it and either reduce the counter or remove it altogether.//
|
||||
if(metadataSet.containsValue(metadata)) {
|
||||
metadata = (Metadata) metadataSet.get(metadata);
|
||||
|
||||
if(metadata.getCounter() != 1) {
|
||||
metadata.setCounter(metadata.getCounter() - 1);
|
||||
}//if//
|
||||
else {
|
||||
metadataSet.remove(metadata);
|
||||
internalRelease(metadata, flags);
|
||||
metadata.release();
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//TODO: What to do about this? The value was never added.
|
||||
Debug.log(new RuntimeException("Cannot remove what wasn't added."));
|
||||
}//else//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters the entity with this binding.
|
||||
* @param metadata The metadata that should be removed. This may be provided in place of the object reference and source when a weakly referenced object is removed from memory.
|
||||
*/
|
||||
public void unregister(Metadata metadata) {
|
||||
metadataSet.remove(metadata);
|
||||
internalRelease(metadata, 0);
|
||||
metadata.release();
|
||||
}//unregister()//
|
||||
/**
|
||||
* Creates a new (uninitialized) metadata object for the given object and source.
|
||||
* @param source The source of the new metadata object.
|
||||
* @param object The object the metadata will describe.
|
||||
* @return The uninitialized metadata for the object. If the metadata isn't used it will be GC'd, otherwise the internalRegister(Metadata, Metadata) method will be called.
|
||||
*/
|
||||
protected abstract Metadata internalCreateMetadata(Metadata source, Object object);
|
||||
/**
|
||||
* Initializes the metadata to be used by this binding & notifies the listeners that the metadata's object exists.
|
||||
* @param metadata The metadata for the object being initialized in the context of this binding.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
protected abstract void internalInitialize(Metadata metadata, int flags);
|
||||
/**
|
||||
* Releases the metadata no longer used by this binding & notifies the listeners that the metadata's object no longer exists.
|
||||
* @param metadata The metadata for the object being released in the context of this binding.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
protected abstract void internalRelease(Metadata metadata, int flags);
|
||||
/**
|
||||
* Called when the binding finds a new value that may not be part of the listener's scope yet.
|
||||
* @param source The source for the value.
|
||||
* @param value The found value.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
protected void valueAdded(Metadata source, Object value, int flags) {
|
||||
listener.valueAdded(source, value, flags);
|
||||
}//valueAdded()//
|
||||
/**
|
||||
* Called when the binding no longer references the previously found value.
|
||||
* @param source The source for the value.
|
||||
* @param value The value that may no longer be in the listener's scope.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
protected void valueRemoved(Metadata source, Object value, int flags) {
|
||||
listener.valueRemoved(source, value, flags);
|
||||
}//valueRemoved()//
|
||||
/**
|
||||
* Provides the binding an opportunity to perform pre-change processing.
|
||||
*/
|
||||
protected void changeStarted() {
|
||||
listener.changeStarted();
|
||||
}//changeStarted()//
|
||||
/**
|
||||
* Provides the binding an opportunity to perform post-change processing.
|
||||
*/
|
||||
protected void changeStopped() {
|
||||
listener.changeStopped();
|
||||
}//changeStopped()//
|
||||
/**
|
||||
* Generates debug output for the binding.
|
||||
* @param buffer The debug output buffer.
|
||||
* @param source The source for the value.
|
||||
* @param value The found value.
|
||||
* @param tabDepth The number of tab indents.
|
||||
* @param handler The optional handler used to generate human readable strings for referenced values.
|
||||
*/
|
||||
protected void debug(StringBuffer buffer, Metadata source, Object value, int tabDepth, ModelListener.DebugHandler handler) {
|
||||
listener.debug(buffer, value, source, tabDepth, handler);
|
||||
}//debug()//
|
||||
/**
|
||||
* Generates debug output for the binding.
|
||||
* @param buffer The debug output buffer.
|
||||
* @param value The found value.
|
||||
* @param tabDepth The number of tab indents.
|
||||
* @param handler The optional handler used to generate human readable strings for referenced values.
|
||||
*/
|
||||
protected abstract void debug(StringBuffer buffer, Binding.Metadata metadata, int tabDepth, ModelListener.DebugHandler handler);
|
||||
}//Binding//
|
||||
277
Foundation/src/com/foundation/event/model/CollectionBinding.java
Normal file
277
Foundation/src/com/foundation/event/model/CollectionBinding.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.foundation.util.IInlineCollectionObservable;
|
||||
import com.foundation.util.IInlineCollectionObserver;
|
||||
|
||||
public class CollectionBinding extends Binding {
|
||||
protected static class CollectionMetadata extends Binding.Metadata implements IInlineCollectionObserver {
|
||||
/** A temporary handler reference which is used to notify the listener of a set of changes. */
|
||||
private IModelListenerCollectionHandler handler = null;
|
||||
/** A temporary added values collection which is used to notify the listener of a set of changes. */
|
||||
private IList addedValues = null;
|
||||
/** A temporary removed values collection which is used to notify the listener of a set of changes. */
|
||||
private IList removedValues = null;
|
||||
/** This is normally zero but may be non-zero while the listener is initializing. It is assumed that the collection is locked such that durring initialization only the one thread can make collection changes. */
|
||||
private int flags = 0;
|
||||
/** Whether the changes should be held so they can be dealt with in bulk when the stopChanges method is called. */
|
||||
private boolean holdChanges = false;
|
||||
|
||||
/**
|
||||
* CollectionMetadata constructor.
|
||||
* @param binding The binding that created this metadata.
|
||||
* @param source The source binding that generated the entity this binding references. This may be null if the entity is a root of the listener.
|
||||
* @param collection The collection the metadata describes.
|
||||
*/
|
||||
public CollectionMetadata(Binding binding, Binding.Metadata source, ICollection collection) {
|
||||
super(binding, source, collection);
|
||||
}//CollectionMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Binding.Metadata#getCollectionModelListener(int)
|
||||
*/
|
||||
protected IModelListenerCollectionHandler getCollectionModelListener(int collectionDepth) {
|
||||
return getSource().getCollectionModelListener(collectionDepth + 1);
|
||||
}//getCollectionModelListener()//
|
||||
/*
|
||||
* Collects the collection model listeners for this collection.
|
||||
* @param listeners The set of unique listeners for this collection.
|
||||
*
|
||||
public IModelListenerCollectionHandler getCollectionModelListener() {
|
||||
return getSource() != null ? getSource().getCollectionModelListener(0) : null;
|
||||
}//getCollectionModelListener()//
|
||||
*/
|
||||
/**
|
||||
* Gets the handler notified if the collection is altered.
|
||||
* @return The handler to be notified of changes.
|
||||
*/
|
||||
public IModelListenerCollectionHandler getHandler() {
|
||||
return handler;
|
||||
}//getHandler()//
|
||||
/**
|
||||
* Sets the handler notified if the collection is altered.
|
||||
* @param handler The handler to be notified of changes.
|
||||
*/
|
||||
public void setHandler(IModelListenerCollectionHandler handler) {
|
||||
this.handler = handler;
|
||||
}//setHandler()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#startChanges(int)
|
||||
*/
|
||||
public void startChanges(int changeCount) {
|
||||
holdChanges = true;
|
||||
|
||||
//If there is a listener then prepare to save the adds & removes.//
|
||||
if(handler != null) {
|
||||
addedValues = new LiteList(changeCount > 0 ? changeCount : 100);
|
||||
removedValues = new LiteList(changeCount > 0 ? changeCount : 100);
|
||||
}//if//
|
||||
|
||||
getBinding().changeStarted();
|
||||
}//startChanges()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#stopChanges()
|
||||
*/
|
||||
public void stopChanges() {
|
||||
holdChanges = false;
|
||||
|
||||
//Notify the handler of the changes.//
|
||||
if(handler != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
|
||||
|
||||
handler.run(getObject(handler.getReferenceType(), true), handlerFlags, addedValues, removedValues);
|
||||
//Cleanup the added/removed collections.//
|
||||
addedValues.removeAll();
|
||||
addedValues = null;
|
||||
removedValues.removeAll();
|
||||
removedValues = null;
|
||||
}//while//
|
||||
|
||||
//Notify the binding the change stopped.//
|
||||
getBinding().changeStopped();
|
||||
}//stopChanges()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#valueAdded(java.lang.Object)
|
||||
*/
|
||||
public void valueAdded(Object value) {
|
||||
if(addedValues != null) {
|
||||
addedValues.add(value);
|
||||
}//if//
|
||||
|
||||
getBinding().valueAdded(this, value, flags);
|
||||
|
||||
//Notify the handler of the changes.//
|
||||
if(!holdChanges && handler != null) {
|
||||
handler.run(getObject(handler.getReferenceType(), true), 0, new LiteList((Object) value), LiteList.EMPTY_LIST);
|
||||
}//if//
|
||||
}//valueAdded()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#valueRemoved(java.lang.Object)
|
||||
*/
|
||||
public void valueRemoved(Object value) {
|
||||
if(removedValues != null) {
|
||||
removedValues.add(value);
|
||||
}//if//
|
||||
|
||||
getBinding().valueRemoved(this, value, flags);
|
||||
|
||||
//Notify the handler of the changes.//
|
||||
if(!holdChanges && handler != null) {
|
||||
handler.run(getObject(handler.getReferenceType(), true), 0, LiteList.EMPTY_LIST, new LiteList((Object) value));
|
||||
}//if//
|
||||
}//valueRemoved()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#allRemoved(com.common.util.IList)
|
||||
*/
|
||||
public void removingAll() {
|
||||
LiteList removed = new LiteList((ICollection) getObject());
|
||||
|
||||
if(removedValues != null) {
|
||||
removedValues.addAll(removed);
|
||||
}//if//
|
||||
|
||||
for(int index = 0; index < removed.getSize(); index++) {
|
||||
getBinding().valueRemoved(this, removed.get(index), flags);
|
||||
}//for//
|
||||
|
||||
//Notify the handler of the changes.//
|
||||
if(!holdChanges && handler != null) {
|
||||
//Note that this method is called PRIOR to a removeAll, so the actual collection will still have its values at the time of this line's execution.//
|
||||
handler.run(getObject(handler.getReferenceType(), true), 0, LiteList.EMPTY_LIST, removed);
|
||||
}//if//
|
||||
}//removingAll()//
|
||||
}//AttributeEntityMetadata//
|
||||
/**
|
||||
* Determines whether the event is occuring while the model listener is in the process of initializing.
|
||||
* @param flags The flags from the IModelListenerAttributeHandler.
|
||||
* @return Whether the listener is initializing.
|
||||
*/
|
||||
public static boolean isListenerInitializing(int flags) {
|
||||
return (flags & IModelListenerCollectionHandler.FLAG_LISTENER_INITIALIZING) > 0;
|
||||
}//isListenerInitializing()//
|
||||
/**
|
||||
* Determines whether the event is occuring while the model listener is in the process of releasing.
|
||||
* @param flags The flags from the IModelListenerAttributeHandler.
|
||||
* @return Whether the listener is releasing.
|
||||
*/
|
||||
public static boolean isListenerReleasing(int flags) {
|
||||
return (flags & IModelListenerCollectionHandler.FLAG_LISTENER_RELEASING) > 0;
|
||||
}//isListenerReleasing()//
|
||||
/**
|
||||
* CollectionBinding constructor.
|
||||
* @param listener The listener that this binding is associated with.
|
||||
*/
|
||||
CollectionBinding(ModelListener listener) {
|
||||
super(null, 0);
|
||||
setListener(listener);
|
||||
}//CollectionBinding()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Binding#internalCreateMetadata(com.foundation.common.Binding.Metadata, java.lang.Object)
|
||||
*/
|
||||
protected Metadata internalCreateMetadata(Metadata source, Object object) {
|
||||
return new CollectionMetadata(this, source, (ICollection) object);
|
||||
}//internalCreateMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#internalInitialize(com.foundation.event.model.Binding.Metadata, int)
|
||||
*/
|
||||
protected void internalInitialize(Binding.Metadata metadata, int flags) {
|
||||
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
|
||||
ICollection collection = (ICollection) collectionMetadata.getObject();
|
||||
IModelListenerCollectionHandler handler = collectionMetadata.getHandler();
|
||||
|
||||
//Start listening for collection changes. Note: This generates add events which are captured by the CollectionMetadata and which will add the value to the listener scope.//
|
||||
if(collection instanceof IInlineCollectionObservable) {
|
||||
collectionMetadata.flags = flags;
|
||||
((IInlineCollectionObservable) collection).addCollectionObserver(collectionMetadata);
|
||||
|
||||
//If the collection has no elements then the add observer will optimize and not notify the listener. We need the listener notified regardless of the element count, so do so now.//
|
||||
if(collection.getSize() == 0 && handler != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
|
||||
|
||||
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, LiteList.EMPTY_LIST, LiteList.EMPTY_LIST);
|
||||
}//if//
|
||||
|
||||
collectionMetadata.flags = 0;
|
||||
}//if//
|
||||
else {
|
||||
IList values = new LiteList(collection, 200, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
|
||||
|
||||
//Add the collection values to the listener.//
|
||||
for(int index = 0; index < values.getSize(); index++) {
|
||||
valueAdded(collectionMetadata, values.get(index), flags);
|
||||
}//for//
|
||||
|
||||
//If there is a listener then notify of the new collection values.//
|
||||
if(handler != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_INITIALIZING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_INITIALIZING : 0;
|
||||
|
||||
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, values, LiteList.EMPTY_LIST);
|
||||
}//if//
|
||||
}//else//
|
||||
}//internalInitialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#internalRelease(com.foundation.event.model.Binding.Metadata, int)
|
||||
*/
|
||||
protected void internalRelease(Binding.Metadata metadata, int flags) {
|
||||
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
|
||||
ICollection collection = (ICollection) metadata.getObject();
|
||||
IModelListenerCollectionHandler handler = collectionMetadata.getHandler();
|
||||
IList values = new LiteList(collection, 200, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
|
||||
|
||||
//Stop listening for collection changes. Note: Unlike the addCollectionObserver(..) call, this does not generate remove events.//
|
||||
if(collection instanceof IInlineCollectionObservable) {
|
||||
((IInlineCollectionObservable) collection).removeCollectionObserver((CollectionMetadata) metadata);
|
||||
}//if//
|
||||
|
||||
//Remove the collection values from the listener.//
|
||||
for(int index = 0; index < values.getSize(); index++) {
|
||||
valueRemoved(metadata, values.get(index), flags);
|
||||
}//for//
|
||||
|
||||
//If there is a listener then notify of the removed collection values.//
|
||||
if(handler != null) {
|
||||
int handlerFlags = ((flags & ModelListener.INTERNAL_FLAG_RELEASING) > 0) ? IModelListenerAttributeHandler.FLAG_LISTENER_RELEASING : 0;
|
||||
|
||||
handler.run(metadata.getObject(handler.getReferenceType(), true), handlerFlags, values, LiteList.EMPTY_LIST);
|
||||
}//if//
|
||||
}//internalRelease()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#getMetadata(java.lang.Object, com.foundation.event.model.Binding)
|
||||
*/
|
||||
protected Metadata getMetadata(Object value, Metadata source) {
|
||||
return getMetadata(new CollectionMetadata(this, source, (ICollection) value));
|
||||
}//getMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.model.Binding#debug(java.lang.StringBuffer, com.foundation.event.model.Binding.Metadata, int, com.foundation.event.model.ModelListener.DebugHandler)
|
||||
*/
|
||||
protected void debug(StringBuffer buffer, Binding.Metadata metadata, int tabDepth, ModelListener.DebugHandler handler) {
|
||||
CollectionMetadata collectionMetadata = (CollectionMetadata) metadata;
|
||||
ICollection collection = (ICollection) collectionMetadata.getObject();
|
||||
IList values = new LiteList(collection, false); //Copy the collection since it might change while we are adding the values to the model listener (due to our possibly loosing our lock on the collection's monitor).//
|
||||
|
||||
for(int index = 0; index < tabDepth; index++) {
|
||||
buffer.append('\t');
|
||||
}//for//
|
||||
|
||||
buffer.append("Collection {");
|
||||
buffer.append("\r\n");
|
||||
|
||||
//Navigate to the value's bindings.//
|
||||
for(int index = 0; index < values.getSize(); index++) {
|
||||
debug(buffer, collectionMetadata, values.get(index), tabDepth + 1, handler);
|
||||
}//for//
|
||||
|
||||
for(int index = 0; index < tabDepth; index++) {
|
||||
buffer.append('\t');
|
||||
}//for//
|
||||
|
||||
buffer.append("}");
|
||||
buffer.append("\r\n");
|
||||
}//debug()//
|
||||
}//CollectionBinding//
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
public interface IModelListenerAttributeHandler extends IModelListenerHandler {
|
||||
/** This type indicates that a the attribute's parent was added to the model listener's scope and thus while the attribute's value may not have changed, the listener may still be interested in knowing it now exists. */
|
||||
public static final int TYPE_ADDED = 0;
|
||||
/** This type indicates that the attribute's parent was removed from the model listener's scope and thus while the attribute's value may not have changed, the listener may still be interested in knowing it isn't part of the scope any more. */
|
||||
public static final int TYPE_REMOVED = 1;
|
||||
/** This type indicates that the attribute's value has changed due to it being lazily initialized. The old value will always be NOT_SET. */
|
||||
public static final int TYPE_INITIALIZED = 2;
|
||||
/** This type indicates that the attribute's value has been changed while the model listener was observing it. This is the most common event type. */
|
||||
public static final int TYPE_CHANGED = 3;
|
||||
|
||||
/**
|
||||
* This flag is only used in conjunction with the TYPE_ADDED or TYPE_REMOVED types and it indicates that the newValue or oldValue will be NOT_SET because the attribute's value has not been initialized.
|
||||
* This is never set if the AttributeBinding was created with the FLAG_LAZY_LOAD_ATTRIBUTE flag set.
|
||||
* @see AttributeBinding.FLAG_LAZY_LOAD_ATTRIBUTE
|
||||
* @see #TYPE_ADDED
|
||||
* @see #TYPE_REMOVED
|
||||
*/
|
||||
public static final int FLAG_ATTRIBUTE_UNINITIALIZED = 0x01;
|
||||
/** This flag is set if the model listener is in the process of initializing. During the listener's initializing a lot of TYPE_ADDED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
|
||||
public static final int FLAG_LISTENER_INITIALIZING = 0x02;
|
||||
/** This flag is set if the model listener is in the process of releasing. During the listener's releasing a lot of TYPE_REMOVED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
|
||||
public static final int FLAG_LISTENER_RELEASING = 0x04;
|
||||
|
||||
/** This object is passed in place of the old or new value parameters if the attribute is or has not been set yet (it has not yet been initialized to some value). */
|
||||
public static final Object NOT_SET = new Object();
|
||||
/**
|
||||
* Notifies the runnable that the event being listened for occured.
|
||||
* <p><b>Warning: This handler is called inline by the thread that is performing the changes. The thread has (if coded correctly) locked the collection's monitor.</b></p>
|
||||
* @param object The object of the type returned by getReferenceType(), or null if an object of this type was not found by the listener in the hierarchy for the event.
|
||||
* @param type One of the type codes defined by this listener class. This can either be TYPE_REMOVED, TYPE_ADDED, or TYPE_CHANGED indicating that the attribute changed, or the path to the attribute was removed, or the path to the attribute is new.
|
||||
* @param flags Zero or more of the flags identifiers.
|
||||
* @param oldValue The previous value of the attribute, only valid if a single attribute is being listened to by this handler. This may be the constant NOT_SET if the attribute was not previously assigned a value.
|
||||
* @param newValue The new value of the attribute, only valid if a single attribute is being listened to by this handler. This may be the constant NOT_SET if the type is TYPE_ADDED and the attribute has not yet been assigned a value.
|
||||
*/
|
||||
public void run(Object object, int type, int flags, Object oldValue, Object newValue);
|
||||
}//IModelListenerAttributeHandler//
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
/**
|
||||
* Used by the model listener to notify listeners when anything changes.
|
||||
* No notification of exactly what changed since that would be quite difficult.
|
||||
* Notification is always performed after all the changes have been dealt with so that calls to the handler are minimized.
|
||||
* <p>Note: Notification is provided on a different thread than the one that caused the change. This is done to prevent deadlocks if the calling thread holds locks on a part of the model.</p>
|
||||
*/
|
||||
public interface IModelListenerChangeHandler extends Runnable {
|
||||
}//IModelListenerChangeHandler//
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
import com.common.util.IList;
|
||||
|
||||
public interface IModelListenerCollectionHandler extends IModelListenerHandler {
|
||||
/** This flag is set if the model listener is in the process of initializing. During the listener's initializing a lot of TYPE_ADDED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
|
||||
public static final int FLAG_LISTENER_INITIALIZING = 0x02;
|
||||
/** This flag is set if the model listener is in the process of releasing. During the listener's releasing a lot of TYPE_REMOVED events may be fired as it discovers the objects it is listening to. The user may or may not be interested in these events. */
|
||||
public static final int FLAG_LISTENER_RELEASING = 0x04;
|
||||
/**
|
||||
* Notifies the runnable that the event being listened for occured.
|
||||
* <p><b>Warning: This handler is called inline by the thread that is performing the changes. The thread has (if coded correctly) locked the collection's monitor.</b></p>
|
||||
* @param object The object of the type returned by getReferenceType(), or null if an object of this type was not found by the listener in the hierarchy for the event.
|
||||
* @param flags Zero or more of the flags identifiers.
|
||||
* @param addedValues The values added to the collection. This is also called if a collection is added to the model listener's scope.
|
||||
* @param removedValues The values removed from the collection. This is also called if a collection is removed from the model listener's scope.
|
||||
*/
|
||||
public void run(Object object, int flags, IList addedValues, IList removedValues);
|
||||
}//IModelListenerCollectionHandler//
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.event.model;
|
||||
|
||||
public interface IModelListenerHandler {
|
||||
/**
|
||||
* Gets the type of object that the listener should pass to the handler when invoked.
|
||||
* <p>Scenario: The listener is listening to a collection of Project objects and each project's name attribute.
|
||||
* The name attribute's binding specifies a handler which wants to be notified when a project is added, removed, or the name changes.
|
||||
* This method would return Project.class so that the handler is passed a reference to the project whose name changed, got added, or was removed.</p>
|
||||
* @return The type of object reference to be passed to the handler when invoked, or null if the nearest object should be passed.
|
||||
*/
|
||||
public Class getReferenceType();
|
||||
}//IModelListenerHandler//
|
||||
456
Foundation/src/com/foundation/event/model/ModelListener.java
Normal file
456
Foundation/src/com/foundation/event/model/ModelListener.java
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2008 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.event.model;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import com.common.thread.Monitor;
|
||||
import com.common.thread.Scheduler;
|
||||
import com.common.thread.ThreadService;
|
||||
import com.common.util.*;
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.event.*;
|
||||
import com.foundation.metadata.ISupportsContainment;
|
||||
|
||||
/**
|
||||
* A model listener is often used to listen to changes in a model object or in a hierarchy of model objects.
|
||||
* It is capable of digging arbitrarily deep into the model based on the bindings provided.
|
||||
* A listener can be initialized and released when no longer needed, or it can be initialized with the model and allowed to exist as long as the model exists (the model must retain a reference to the listener otherwise GC will have its way).
|
||||
* All listener/model references are weak references and thus will not keep the model in memory.
|
||||
* The bindings for an attribute will not cause the attribute to be loaded if it has not yet been realized.
|
||||
* <p>
|
||||
* To use the model listener, the application must:
|
||||
* <ol>
|
||||
* <li>Create a new ModelListener passing the initial value or values to be listened to, and an optional change hander that will be called any time any binding is altered in any way.
|
||||
* <li>Create and add new bindings. Currently there is only one binding type useable which is AttributeBinding which takes an IEntity class, an attribute defined by the class, an optional flags (usually zero), an optional change handler (usually used to get attribute change notification), and an optional array of change handlers used only if the attribute's value is a collection.
|
||||
* <li>Call the initialize method on the ModelListener.
|
||||
* <li>Save a reference to the ModelListener so it doesn't get GC'd.
|
||||
* <li>Optionally release the ModelListener when it is no longer needed. GC will take care of this if the reference to the ModelListener is lost.
|
||||
* </ol>
|
||||
*/
|
||||
public class ModelListener {
|
||||
public static final int INTERNAL_FLAG_INITIALIZING = 1;
|
||||
public static final int INTERNAL_FLAG_RELEASING = 2;
|
||||
|
||||
/** The collection of bindings that are listened to. */
|
||||
private IList bindings = new LiteList(5, 15);
|
||||
/** The collection binding that the listener uses specially for ICollection values. */
|
||||
private CollectionBinding collectionBinding = new CollectionBinding(this);
|
||||
/** Whether the listener has been initialized. Once initialized the listener begins to listen for changes, bindings cannot be changed and listeners cannot be added, and must be released to avoid memory leaks. */
|
||||
private volatile boolean isInitialized = false;
|
||||
/** The event support that will manage the event registration for this listener. */
|
||||
private EventSupport eventSupport = new EventSupport(null);
|
||||
/** The root object that starts the listener off. This is null if the listener has a parent listener. */
|
||||
private Object rootObject = null;
|
||||
/** The optional listener to be notified when any change occurs in the model. */
|
||||
private IModelListenerChangeHandler changeHandler = null;
|
||||
/** A counter used to determine when a set of changes has been completed. */
|
||||
private int changeStartCount = 0;
|
||||
/** Whether to inline the calls to the global change handler. */
|
||||
private boolean inlineGlobalChangeHandler = false;
|
||||
|
||||
/** The reference queue used to quickly clean up after metadata that references objects no longer in memory. */
|
||||
private static final ReferenceQueue referenceQueue = new ReferenceQueue();
|
||||
//Setup the scheduled task to cleanup the reference queue for all model listeners.//
|
||||
static {
|
||||
Scheduler.addTask(10000, new CleanupTask(referenceQueue), false);
|
||||
}//static//
|
||||
|
||||
/**
|
||||
* The cleanup task used to release bindings that reference values no longer in memory.
|
||||
*/
|
||||
private static class CleanupTask extends Scheduler.Task {
|
||||
private ReferenceQueue queue = null;
|
||||
/**
|
||||
* CleanupTask constructor.
|
||||
* @param listener The model listener which will be weakly referenced so that the listener can be GC'd and this task will know to be released.
|
||||
* @param queue The reference queue to be listening to.
|
||||
*/
|
||||
public CleanupTask(ReferenceQueue queue) {
|
||||
super();
|
||||
this.queue = queue;
|
||||
}//CleanupTask()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.thread.Scheduler.Task#evaluate()
|
||||
*/
|
||||
public void evaluate() {
|
||||
Binding.ObjectReference reference = null;
|
||||
|
||||
//Remove all bindings for objects no longer in memory.//
|
||||
while((reference = (Binding.ObjectReference) queue.poll()) != null) {
|
||||
reference.getMetadata().getBinding().unregister(reference.getMetadata());
|
||||
}//while//
|
||||
}//evaluate()//
|
||||
}//CleanupTask//
|
||||
|
||||
/**
|
||||
* Used by the debug code to create human readable strings for values referenced by the model listener.
|
||||
*/
|
||||
public static abstract class DebugHandler {
|
||||
public abstract String toString(Object value);
|
||||
public String defaultToString(Object value) {
|
||||
return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode());
|
||||
}//defaultToString()//
|
||||
}//IDebugHandler//
|
||||
/**
|
||||
* ModelListener constructor.
|
||||
* @param entity The entity that is the root object for the listener.
|
||||
*/
|
||||
public ModelListener(IEntity entity) {
|
||||
this((Object) entity, null, false);
|
||||
}//ModelListener()//
|
||||
/**
|
||||
* ModelListener constructor.
|
||||
* @param entity The entity that is the root object for the listener.
|
||||
* @param changeHandler The optional handler called if any binding in the listener is updated.
|
||||
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
|
||||
*/
|
||||
public ModelListener(IEntity entity, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
|
||||
this((Object) entity, changeHandler, inlineGlobalChangeHandler);
|
||||
}//ModelListener()//
|
||||
/**
|
||||
* ModelListener constructor.
|
||||
* @param collection The collection that is the root object for the listener.
|
||||
*/
|
||||
public ModelListener(ICollection collection) {
|
||||
this((Object) collection, null, false);
|
||||
}//ModelListener()//
|
||||
/**
|
||||
* ModelListener constructor.
|
||||
* @param collection The collection that is the root object for the listener.
|
||||
* @param changeHandler The optional handler called if any binding in the listener is updated.
|
||||
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
|
||||
*/
|
||||
public ModelListener(ICollection collection, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
|
||||
this((Object) collection, changeHandler, inlineGlobalChangeHandler);
|
||||
}//ModelListener()//
|
||||
/**
|
||||
* ModelListener constructor.
|
||||
* @param rootObject The object which is used by the listener as the initial value.
|
||||
* @param changeHandler The optional handler called if any binding in the listener is updated.
|
||||
* @param inlineGlobalChangeHandler Whether the calls to the change handler are inlined instead of (default) threaded.
|
||||
*/
|
||||
private ModelListener(Object rootObject, IModelListenerChangeHandler changeHandler, boolean inlineGlobalChangeHandler) {
|
||||
this.rootObject = rootObject;
|
||||
this.changeHandler = changeHandler;
|
||||
this.inlineGlobalChangeHandler = inlineGlobalChangeHandler;
|
||||
}//ModelListener()//
|
||||
/**
|
||||
* Gets whether the listener has been initialized yet.
|
||||
* @return The initialization state for this listener.
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
return isInitialized;
|
||||
}//isInitialized()//
|
||||
/**
|
||||
* Gets the event support used by the listener and all its bindings.
|
||||
* @return The shared event support object used by bindings to detect changes in the model.
|
||||
*/
|
||||
protected EventSupport getEventSupport() {
|
||||
return eventSupport;
|
||||
}//getEventSupport()//
|
||||
/**
|
||||
* Called by the bindings when they detect a change.
|
||||
*/
|
||||
protected void changeStarted() {
|
||||
if(changeHandler != null) {
|
||||
internalChangeStarted();
|
||||
}//if//
|
||||
}//changeStarted()//
|
||||
/**
|
||||
* Notifies the listener when a change to the model has completed.
|
||||
*/
|
||||
protected void changeStopped() {
|
||||
if(changeHandler != null) {
|
||||
internalChangeStopped();
|
||||
}//if//
|
||||
}//changeStopped()//
|
||||
/**
|
||||
* Called by the bindings when they detect a change.
|
||||
*/
|
||||
private synchronized void internalChangeStarted() {
|
||||
changeStartCount++;
|
||||
}//internalChangeStarted()//
|
||||
/**
|
||||
* Notifies the listener when a change to the model has completed.
|
||||
*/
|
||||
private synchronized void internalChangeStopped() {
|
||||
changeStartCount--;
|
||||
|
||||
if(changeStartCount == 0) {
|
||||
if(inlineGlobalChangeHandler) {
|
||||
changeHandler.run();
|
||||
}//if//
|
||||
else {
|
||||
ThreadService.run(changeHandler);
|
||||
}//else//
|
||||
}//if//
|
||||
}//internalChangeStopped()//
|
||||
/**
|
||||
* Adds an attribute binding.
|
||||
* <p>Note: This is not thread safe since the same thread that created this listener should be the one to initialize it prior to making is shared.</p>
|
||||
* @param binding The binding to be added to this listener.
|
||||
*/
|
||||
public void addBinding(Binding binding) {
|
||||
if(!isInitialized) {
|
||||
binding.setListener(this);
|
||||
bindings.add(binding);
|
||||
}//if//
|
||||
else {
|
||||
//Cannot add bindings after the listener has been initialized.//
|
||||
throw new IllegalArgumentException();
|
||||
}//else//
|
||||
}//addBinding()//
|
||||
/**
|
||||
* Initializes the listener once the listener has been completely setup.
|
||||
* Once initialized the bindings may not be changed.
|
||||
* After initialization, the listeners will be attached to the data and the specified listener runnables will be called when changes occur.
|
||||
*/
|
||||
public void initialize() {
|
||||
if((rootObject != null) && (!isInitialized)) {
|
||||
isInitialized = true;
|
||||
changeStarted();
|
||||
valueAdded(null, rootObject, INTERNAL_FLAG_INITIALIZING);
|
||||
changeStopped();
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Cannot initialize what has already been initialized.");
|
||||
}//else//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the listeners attached to the data. Once released this listener can be re-initialized.
|
||||
* <p>
|
||||
* Note: It is not required that the release method be called.
|
||||
* This is provided to allow a listener that isn't required any more to be released.
|
||||
* If the listener is no longer referenced then it will be released automatically when the GC thread gets to it.
|
||||
* </p>
|
||||
*/
|
||||
public void release() {
|
||||
if((rootObject != null) && (isInitialized)) {
|
||||
changeStarted();
|
||||
valueRemoved(null, rootObject, INTERNAL_FLAG_RELEASING);
|
||||
changeStopped();
|
||||
isInitialized = false;
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Cannot release what has not been initialized.");
|
||||
}//else//
|
||||
}//release()//
|
||||
/**
|
||||
* Called when a value enters the scope of this listener.
|
||||
* @param source The metadata for the entity which produced the value.
|
||||
* @param value The value to listen to and whose subcomponents may become values in this listener's scope.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
void valueAdded(Binding.Metadata source, Object value, int flags) {
|
||||
if(value != null) {
|
||||
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
|
||||
boolean hasBinding = false;
|
||||
|
||||
try {
|
||||
if(value instanceof ICollection) {
|
||||
CollectionBinding.CollectionMetadata collectionMetadata;
|
||||
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
hasBinding = true;
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
|
||||
//Treat collections specially since there is only one binding we are interested in (the collection values).//
|
||||
collectionMetadata = (CollectionBinding.CollectionMetadata) collectionBinding.register(source, value, referenceQueue, flags);
|
||||
|
||||
if(collectionMetadata != null) {
|
||||
int index = 0;
|
||||
Binding.Metadata nextSource = source;
|
||||
IModelListenerCollectionHandler[] handlers;
|
||||
|
||||
//Get the right collection handler for the source and value.//
|
||||
while(nextSource instanceof CollectionBinding.CollectionMetadata) {
|
||||
nextSource = nextSource.getSource();
|
||||
index++;
|
||||
}//while//
|
||||
|
||||
handlers = nextSource == null ? null : nextSource.getBinding().getCollectionHandlers();
|
||||
collectionMetadata.setHandler(handlers != null && handlers.length > index ? handlers[index] : null);
|
||||
collectionBinding.internalInitialize(collectionMetadata, flags);
|
||||
}//if//
|
||||
}//if//
|
||||
else if(value instanceof IEntity) {
|
||||
IEntity entity = (IEntity) value;
|
||||
|
||||
//Apply the entity to any bindings it matches.//
|
||||
for(int index = 0; index < bindings.getSize(); index++) {
|
||||
Binding binding = (Binding) bindings.get(index);
|
||||
|
||||
if(binding.matches(entity, hasBinding)) {
|
||||
Binding.Metadata bindingMetadata;
|
||||
|
||||
//If this is the first binding we have found then set the flag and check the locks.//
|
||||
if(!hasBinding) {
|
||||
hasBinding = true;
|
||||
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
bindingMetadata = binding.register(source, entity, referenceQueue, flags);
|
||||
|
||||
if(bindingMetadata != null) {
|
||||
binding.internalInitialize(bindingMetadata, flags);
|
||||
}//if//
|
||||
}//if//
|
||||
}//for//
|
||||
}//else//
|
||||
}//try//
|
||||
finally {
|
||||
//Re-lock on the old monitor if necessary.//
|
||||
if((hasBinding) && (monitor != null)) {
|
||||
Monitor.unlock(monitor);
|
||||
}//if//
|
||||
}//finally//
|
||||
}//if//
|
||||
}//valueAdded()//
|
||||
/**
|
||||
* Called when a value is removed from the scope of this listener.
|
||||
* @param source The metadata for the entity which produced the value.
|
||||
* @param value The value to stop listening on and remove from the scope of this listener. All subcomponents will also no longer be in this listener's scope.
|
||||
* @param flags The flags for the operation, or zero.
|
||||
*/
|
||||
void valueRemoved(Binding.Metadata source, Object value, int flags) {
|
||||
if(value != null) {
|
||||
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
|
||||
boolean hasBinding = false;
|
||||
|
||||
if(value instanceof ICollection) {
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
|
||||
//Treat collections specially since there is only one binding we are interested in (the collection values).//
|
||||
collectionBinding.unregister(source, value, flags);
|
||||
hasBinding = true;
|
||||
}//if//
|
||||
else if(value instanceof IEntity) {
|
||||
IEntity entity = (IEntity) value;
|
||||
|
||||
//Apply the entity to any bindings it matches.//
|
||||
for(int index = 0; index < bindings.getSize(); index++) {
|
||||
Binding binding = (Binding) bindings.get(index);
|
||||
|
||||
if(binding.matches(entity, hasBinding)) {
|
||||
//If this is the first binding we have found then set the flag and check the locks.//
|
||||
if(!hasBinding) {
|
||||
hasBinding = true;
|
||||
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
binding.unregister(source, entity, flags);
|
||||
}//if//
|
||||
}//for//
|
||||
}//else//
|
||||
|
||||
//Re-lock on the old monitor if necessary.//
|
||||
if((hasBinding) && (monitor != null)) {
|
||||
Monitor.unlock(monitor);
|
||||
}//if//
|
||||
}//if//
|
||||
}//valueRemoved()//
|
||||
/**
|
||||
* Generates a debug string for the model listener's registered models.
|
||||
* @param buffer The output buffer.
|
||||
* @param handler The optional handler used to generate human readable strings from objects referenced by the model listener.
|
||||
* @param initialTabDepth The initial depth to tab the resulting string to.
|
||||
*/
|
||||
public void debug(StringBuffer buffer, DebugHandler handler, int initialTabDepth) {
|
||||
debug(buffer, rootObject, null, initialTabDepth, handler == null ? new DebugHandler() {
|
||||
public String toString(Object value) {
|
||||
return defaultToString(value);
|
||||
}//toString()//
|
||||
} : handler);
|
||||
}//debug()//
|
||||
/**
|
||||
* Generates a debug string for the model listener's registered models.
|
||||
* @param buffer The buffer to use for display.
|
||||
* @param value The value to display and whose bindings should be navigated.
|
||||
* @param source The value's binding source, or null for root objects.
|
||||
* @param tabDepth The depth of the tab indents.
|
||||
* @param handler The optional handler used to generate human readable strings from objects referenced by the model listener.
|
||||
*/
|
||||
protected void debug(StringBuffer buffer, Object value, Binding.Metadata source, int tabDepth, DebugHandler handler) {
|
||||
if(value != null) {
|
||||
Monitor monitor = value instanceof ISupportsContainment ? ((ISupportsContainment) value).getMonitor() : null;
|
||||
boolean hasBinding = false;
|
||||
|
||||
if(value instanceof ICollection) {
|
||||
CollectionBinding.CollectionMetadata collectionMetadata;
|
||||
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
|
||||
collectionMetadata = (CollectionBinding.CollectionMetadata) collectionBinding.getMetadata(value, source);
|
||||
|
||||
if(collectionMetadata != null) {
|
||||
collectionBinding.debug(buffer, collectionMetadata, tabDepth, handler);
|
||||
}//if//
|
||||
|
||||
hasBinding = true;
|
||||
}//if//
|
||||
else if(value instanceof IEntity) {
|
||||
IEntity entity = (IEntity) value;
|
||||
|
||||
for(int index = 0; index < tabDepth; index++) {
|
||||
buffer.append('\t');
|
||||
}//for//
|
||||
|
||||
buffer.append(handler.toString(entity));
|
||||
buffer.append("\r\n");
|
||||
|
||||
//Apply the entity to any bindings it matches.//
|
||||
for(int index = 0; index < bindings.getSize(); index++) {
|
||||
Binding binding = (Binding) bindings.get(index);
|
||||
|
||||
if(binding.matches(entity, hasBinding)) {
|
||||
Binding.Metadata bindingMetadata;
|
||||
|
||||
//If this is the first binding we have found then set the flag and check the locks.//
|
||||
if(!hasBinding) {
|
||||
hasBinding = true;
|
||||
|
||||
//Lock on the new monitor if possible.//
|
||||
if(monitor != null) {
|
||||
Monitor.lock(monitor);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
bindingMetadata = binding.getMetadata(entity, source);
|
||||
|
||||
if(bindingMetadata != null) {
|
||||
binding.debug(buffer, bindingMetadata, tabDepth + 1, handler);
|
||||
}//if//
|
||||
}//if//
|
||||
}//for//
|
||||
}//else//
|
||||
|
||||
//Re-lock on the old monitor if necessary.//
|
||||
if((hasBinding) && (monitor != null)) {
|
||||
Monitor.unlock(monitor);
|
||||
}//if//
|
||||
}//if//
|
||||
}//debug()//
|
||||
}//ModelListener//
|
||||
24
Foundation/src/com/foundation/exception/EventException.java
Normal file
24
Foundation/src/com/foundation/exception/EventException.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.exception;
|
||||
|
||||
public class EventException extends RuntimeException {
|
||||
/**
|
||||
* EventException constructor.
|
||||
*/
|
||||
public EventException() {
|
||||
super();
|
||||
}//EventException()//
|
||||
/**
|
||||
* EventException constructor.
|
||||
* @param s java.lang.String
|
||||
*/
|
||||
public EventException(String s) {
|
||||
super(s);
|
||||
}//EventException()//
|
||||
}//EventException//
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.exception;
|
||||
|
||||
/**
|
||||
* This exception will be thrown by the foundation classes to indicate when the application has been improperly designed.
|
||||
*/
|
||||
public class FoundationDesignException extends RuntimeException {
|
||||
/**
|
||||
* FoundationDesignException constructor.
|
||||
*/
|
||||
public FoundationDesignException() {
|
||||
super();
|
||||
}//FoundationDesignException()//
|
||||
/**
|
||||
* FoundationDesignException constructor.
|
||||
* @param description The exception's description.
|
||||
*/
|
||||
public FoundationDesignException(String description) {
|
||||
super(description);
|
||||
}//FoundationDesignException()//
|
||||
}//FoundationDesignException//
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.exception;
|
||||
|
||||
public class MaximumSizeExceeded extends RuntimeException {
|
||||
/**
|
||||
* MaximumSizeExceeded constructor.
|
||||
*/
|
||||
public MaximumSizeExceeded() {
|
||||
super();
|
||||
}//MaximumSizeExceeded()//
|
||||
/**
|
||||
* MaximumSizeExceeded constructor.
|
||||
* @param s java.lang.String
|
||||
*/
|
||||
public MaximumSizeExceeded(String s) {
|
||||
super(s);
|
||||
}//MaximumSizeExceeded()//
|
||||
}//MaximumSizeExceeded//
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.exception;
|
||||
|
||||
/**
|
||||
* This exception will be thrown when loading metadata for an application if the metadata source was not properly formatted or corrupted.
|
||||
*/
|
||||
public class MetadataLoadException extends RuntimeException {
|
||||
/**
|
||||
* MetadataLoadException constructor.
|
||||
*/
|
||||
public MetadataLoadException() {
|
||||
super();
|
||||
}//MetadataLoadException()//
|
||||
/**
|
||||
* MetadataLoadException constructor.
|
||||
* @param description The exception's description.
|
||||
*/
|
||||
public MetadataLoadException(String description) {
|
||||
super(description);
|
||||
}//MetadataLoadException()//
|
||||
}//MetadataLoadException//
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.foundation.exception;
|
||||
|
||||
/**
|
||||
* Used to pass a failure to the ReflectionContext's processLocalSynchronizationDataByMonitor(..) method.
|
||||
* In perticular this is used by a collection to report an error while attempting to modify it due to a synchronization.
|
||||
* The problem it solves is in getting a second return value during an error condition that indicates to the ReflectionContext's method that it should set the SynchronizationResponse's failureReason property.
|
||||
* Once set, the failureReason property will be delivered to the synchronizer via the ReflectionManager.
|
||||
*/
|
||||
public class ReflectionSynchronizationException extends RuntimeException {
|
||||
private Object failureReason;
|
||||
/**
|
||||
* ReflectionSynchronizationException constructor.
|
||||
* @param failureReason Any serializeable object (preferably Externalizable or IExternalizable since they are more efficient).
|
||||
*/
|
||||
public ReflectionSynchronizationException(Object failureReason) {
|
||||
this.failureReason = failureReason;
|
||||
}//ReflectionSynchronizationException()//
|
||||
public Object getFailureReason() {
|
||||
return failureReason;
|
||||
}//getFailureReason()//
|
||||
}//ReflectionSynchronizationException//
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.exception;
|
||||
|
||||
import com.foundation.common.IEntity;
|
||||
import com.foundation.transaction.TransactionErrorInfo;
|
||||
import com.foundation.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* This exception is thrown by the transaction foundation during the setup process of a transaction.
|
||||
* Also used to identify problems processing transactions or preparing for them.
|
||||
* This class attempts to serve two purposes (TODO: We should separate the two into distinct exceptions): normal exception processing while setting up a transaction, and error handling when a transaction or preparations for one goes wrong.
|
||||
*/
|
||||
public class TransactionException extends RuntimeException {
|
||||
/** The error information from the transaction's failure. */
|
||||
private TransactionErrorInfo errorInfo;
|
||||
/**
|
||||
* TransactionException constructor.
|
||||
* @param description The exception's description.
|
||||
*/
|
||||
public TransactionException(String description) {
|
||||
super(description);
|
||||
}//TransactionException()//
|
||||
/**
|
||||
* TransactionException constructor.
|
||||
* @param transactionErrorCode One of the error code identifiers (ERROR_xxx) defined by the Transaction class.
|
||||
*/
|
||||
public TransactionException(int transactionErrorCode, String message, Throwable exception) {
|
||||
this(transactionErrorCode, message, exception, null);
|
||||
}//TransactionException()//
|
||||
/**
|
||||
* TransactionException constructor.
|
||||
* @param transactionErrorCode One of the error code identifiers (ERROR_xxx) defined by the Transaction class.
|
||||
*/
|
||||
public TransactionException(int transactionErrorCode, String message, Throwable exception, Object errorData) {
|
||||
super(message, exception);
|
||||
this.errorInfo = new TransactionErrorInfo(transactionErrorCode, IEntity.MODEL_ERROR_NONE, errorData == null ? message : errorData);
|
||||
}//TransactionException()//
|
||||
/**
|
||||
* TransactionException constructor.
|
||||
* @param transactionErrorCode One of the transaction error code identifiers (Transaction.ERROR_xxx).
|
||||
* @param modelErrorCode One of the model error code identifiers (IEntity.MODEL_ERROR_xxx).
|
||||
*/
|
||||
public TransactionException(int transactionErrorCode, int modelErrorCode, String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
this.errorInfo = new TransactionErrorInfo(transactionErrorCode, modelErrorCode, message);
|
||||
}//TransactionException()//
|
||||
/**
|
||||
* Gets the error info object relating to the failed transaction.
|
||||
* @return The transaction generated error information.
|
||||
*/
|
||||
public TransactionErrorInfo getErrorInfo() {
|
||||
return errorInfo;
|
||||
}//getErrorInfo()//
|
||||
/**
|
||||
* Gets the transaction error code that caused the exception, or ERROR_NONE if no model error caused the problem.
|
||||
* @return The ERROR_xxx identifier defined by Transaction.
|
||||
*/
|
||||
public int getTransactionErrorCode() {
|
||||
return errorInfo.getTransactionErrorCode();
|
||||
}//getTransactionErrorCode()//
|
||||
/**
|
||||
* Gets the model error code that caused the exception, or MODEL_ERROR_NONE if no model error caused the problem.
|
||||
* @return The MODEL_ERROR_xxx identifier defined by IEntity.
|
||||
*/
|
||||
public int getModelErrorCode() {
|
||||
return errorInfo.getModelErrorCode();
|
||||
}//getModelErrorCode()//
|
||||
}//TransactionException//
|
||||
93
Foundation/src/com/foundation/metadata/AbstractMetadata.java
Normal file
93
Foundation/src/com/foundation/metadata/AbstractMetadata.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2007 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.metadata;
|
||||
|
||||
public abstract class AbstractMetadata {
|
||||
/** The event or attribute name which should be unique for the type hierarchy. */
|
||||
private String name = null;
|
||||
/** The functional unique number of the attribute or event. Custom and Global (interface) Events begin at an offset to avoid conflicting with attribute events. */
|
||||
private int number = -1;
|
||||
/** The type hierarchy unique number of the attribute or event. */
|
||||
private int hierarchyNumber = -1;
|
||||
/** The declaring type's unique number of the attribute or event. */
|
||||
private int declaredNumber = -1;
|
||||
/**
|
||||
* AbstractMetadata constructor.
|
||||
*/
|
||||
public AbstractMetadata() {
|
||||
}//AbstractMetadata()//
|
||||
/**
|
||||
* Gets the event or attribute name which is used for debugging purposes only.
|
||||
* <p>Note: This value will stay the same for every run of the application and should be used to reference the attribute across process boundaries.
|
||||
* @return The name of this attribute or event.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Sets the event or attribute name.
|
||||
* @param name The name of this event or attribute.
|
||||
*/
|
||||
protected void setName(String name) {
|
||||
this.name = name;
|
||||
}//setName()//
|
||||
/**
|
||||
* Gets the number which will be unique for metadata in the declaring type's hierarchy of metadata.
|
||||
* @return The number that is unique for the declaring type. This is the event number for EventMetadata which can be used to fire or listen for events, and the attribute number used to store or retrieve attribute values.
|
||||
*/
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}//getNumber()//
|
||||
/**
|
||||
* Sets the number which will be unique for metadata in the declaring type's hierarchy of metadata.
|
||||
* @param number The number that is unique for the declaring type. This is the event number for EventMetadata which can be used to fire or listen for events, and the attribute number used to store or retrieve attribute values.
|
||||
*/
|
||||
protected void setNumber(int number) {
|
||||
this.number = number;
|
||||
}//setNumber()//
|
||||
/**
|
||||
* Gets the number which will be unique for metadata in the declaring type's metadata.
|
||||
* @return The number that can be used to easily reference this metadata within the declaring type.
|
||||
*/
|
||||
public int getHierarchyNumber() {
|
||||
return hierarchyNumber;
|
||||
}//getHierarchyNumber()//
|
||||
/**
|
||||
* Sets the number which will be unique for metadata in the declaring type's hierarchy (using eventList or attributeList).
|
||||
* @param hierarchyNumber The number that can be used to easily reference this metadata within the declaring type (using eventList or attributeList).
|
||||
*/
|
||||
protected void setHierarchyNumber(int hierarchyNumber) {
|
||||
this.hierarchyNumber = hierarchyNumber;
|
||||
}//setHierarchyNumber()//
|
||||
/**
|
||||
* Gets the number which will be unique for metadata in the declaring type's metadata.
|
||||
* @return The number that can be used to easily reference this metadata within the declaring type.
|
||||
*/
|
||||
public int getDeclaredNumber() {
|
||||
return declaredNumber;
|
||||
}//getDeclaredNumber()//
|
||||
/**
|
||||
* Sets the number which will be unique for metadata in the declaring type's hierarchy (using declaredEventList or declaredAttributeList).
|
||||
* @param localNumber The number that can be used to easily reference this metadata within the declaring type (using declaredEventList or declaredAttributeList).
|
||||
*/
|
||||
protected void setDeclaredNumber(int declaredNumber) {
|
||||
this.declaredNumber = declaredNumber;
|
||||
}//setDeclaredNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof AbstractMetadata && ((AbstractMetadata) object).getName().equals(getName());
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return getName().hashCode();
|
||||
}//hashCode()//
|
||||
}//AbstractMetadata//
|
||||
157
Foundation/src/com/foundation/metadata/Attribute.java
Normal file
157
Foundation/src/com/foundation/metadata/Attribute.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 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.metadata;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
public class Attribute extends Event {
|
||||
private AttributeMetadata metadata;
|
||||
/**
|
||||
* A convience method to collect a set of all attribute identifiers defined by a type and all its sub-types except a specified set.
|
||||
* @param type The type whose attribute identifiers are to be collected.
|
||||
* @param missing The attributes to not include in the result set. Attributes not of the given type, or duplicates will generate a runtime exception.
|
||||
* @return The set of all attributes.
|
||||
*/
|
||||
public static Attribute[] collect(Class type, Attribute[] missing) {
|
||||
return MetadataService.getSingleton().getTypeMetadata(type).getAttributes(missing);
|
||||
}//getAllExcept()//
|
||||
/**
|
||||
* A convience method to collect a set of all attribute identifiers defined by a type and all its sub-types except a specified set.
|
||||
* @param type The type whose attribute identifiers are to be collected.
|
||||
* @param missing The attributes to not include in the result set. Attributes not of the given type, or duplicates will generate a runtime exception.
|
||||
* @param repositoryIdentifier The identifier of the repository to that collected attributes must have metadata for. This may not be null.
|
||||
* @return The set of all attributes.
|
||||
*/
|
||||
public static Attribute[] collect(Class type, Attribute[] missing, Object repositoryIdentifier) {
|
||||
if(repositoryIdentifier == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}//if//
|
||||
|
||||
return MetadataService.getSingleton().getTypeMetadata(type).getAttributes(missing, repositoryIdentifier);
|
||||
}//getAllExcept()//
|
||||
/**
|
||||
* Attribute constructor.
|
||||
*/
|
||||
protected Attribute(AttributeMetadata attributeMetadata) {
|
||||
super(attributeMetadata);
|
||||
this.metadata = attributeMetadata;
|
||||
}//Attribute()//
|
||||
/**
|
||||
* Gets the metadata associated with the attribute.
|
||||
* @return The metadata for this attribute.
|
||||
*/
|
||||
public AttributeMetadata getAttributeMetadata() {
|
||||
return metadata;
|
||||
}//getAttributeMetadata()//
|
||||
/**
|
||||
* Determines whether the attribute is bound to another attribute.
|
||||
* @return Whether the attribute is bound to another attribute for its value. For example if Address has a CONTACT attribute and Contact has an ID attribute and Address then defines a CONTACT_ID attribute that is bound to the CONTACT attribute's ID attribute.
|
||||
*/
|
||||
public boolean isAttributeBound() {
|
||||
return metadata.isAttributeBound();
|
||||
}//isAttributeBound()//
|
||||
/**
|
||||
* Determines whether the attribute is a back reference, meaning its value is set when another object references the defining class instance as part-of.
|
||||
* @return Whether the attribute's value is based on the part-of reference to the object.
|
||||
*/
|
||||
public boolean isBackReference() {
|
||||
return metadata.isBackReference();
|
||||
}//isBackReference()//
|
||||
/**
|
||||
* Determines whether the attribute value is based on other attribute values and must be recalculated when certain other attributes change their values.
|
||||
* @return Whether the attribute's value is based on the values of other attributes.
|
||||
*/
|
||||
public boolean isCalculated() {
|
||||
return metadata.isCalculated();
|
||||
}//isCalculated()//
|
||||
/**
|
||||
* Determines whether the attribute is a collection attribute.
|
||||
* @return Whether the attribute value is a collection type.
|
||||
*/
|
||||
public boolean isCollection() {
|
||||
return metadata.isCollection();
|
||||
}//isCollection()//
|
||||
/**
|
||||
* Determines whether the attribute is lazily initialized.
|
||||
* @return Whether lazy initialization is required for this attribute.
|
||||
*/
|
||||
public boolean isLazy() {
|
||||
return metadata.isLazy();
|
||||
}//isLazy()//
|
||||
/**
|
||||
* Determines whether the attribute is a collection attribute that requires adds and removes to be recorded using a mapping table (for N to N relationships).
|
||||
* @return Whether the attribute collection of references is mapped via a mapping table and associated class.
|
||||
*/
|
||||
public boolean isMapped() {
|
||||
return metadata.isMapped();
|
||||
}//isMapped()//
|
||||
/**
|
||||
* Determines whether the attribute value is part of the type and thus shares a synchronization monitor.
|
||||
* @return Whether the value is part of which means it only exists within the context of the value's referencing object (this type).
|
||||
*/
|
||||
public boolean isPartOf() {
|
||||
return metadata.isPartOf();
|
||||
}//isPartOf()//
|
||||
/**
|
||||
* Determines whether the attribute value is referenced (versus contained).
|
||||
* @return Whether the value is referenced and it exists independantly of the attribute's object.
|
||||
*/
|
||||
public boolean isReferenced() {
|
||||
return metadata.isReferenced();
|
||||
}//isReferenced()//
|
||||
/**
|
||||
* 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).
|
||||
* Note: Currently calculated attributes should not be reflected since the reflections will need to calculate it themselves.
|
||||
* @return Whether the attribute is to be copied or reflected along with the other attributes when the containing object is reflected.
|
||||
*/
|
||||
public boolean isReflectable() {
|
||||
return metadata.isReflectable();
|
||||
}//isReflectable()//
|
||||
/**
|
||||
* Determines whether the attribute value is reflected as if it were referencing an immutable value.
|
||||
* @return Whether the attribute is to be referenced instead of reflected by the reflection (immutable values are always referenced by the reflection unless marked as non-reflectable).
|
||||
*/
|
||||
public boolean isReflectAsImmutable() {
|
||||
return metadata.isReflectAsImmutable();
|
||||
}//isReflectAsImmutable()//
|
||||
/**
|
||||
* Determines whether the attribute value is not serializable.
|
||||
* @return Whether the value is transient, meaning it does not get serialized automatically with the rest of the attributes.
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
return metadata.isTransient();
|
||||
}//isTransient()//
|
||||
/**
|
||||
* Determines whether the attribute value is weakly referenced.
|
||||
* @return Whether the value wrappered by a weak reference within the attribute subsystem. Normally this should only be true if the attribute has a lazy load handler.
|
||||
*/
|
||||
public boolean isWeak() {
|
||||
return metadata.isWeak();
|
||||
}//isWeak()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
int declaredNumber = in.readInt();
|
||||
Class type = (Class) in.readObject();
|
||||
TypeMetadata typeMetadata = type != null ? MetadataService.getSingleton().getTypeMetadata(type) : null;
|
||||
|
||||
return typeMetadata != null ? typeMetadata.getDeclaredAttributeMetadata(declaredNumber).getEventIdentifier() : IExternalizable.NULL_VALUE;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(metadata.getDeclaredNumber());
|
||||
out.writeObject(metadata.getDeclaringType());
|
||||
}//writeExternal()//
|
||||
}//Attribute//
|
||||
306
Foundation/src/com/foundation/metadata/AttributeMetadata.java
Normal file
306
Foundation/src/com/foundation/metadata/AttributeMetadata.java
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.metadata;
|
||||
|
||||
import com.common.util.*;
|
||||
|
||||
/**
|
||||
* This class manages the metadata for an attribute of a class in the software system.
|
||||
*/
|
||||
public class AttributeMetadata extends EventMetadata {
|
||||
public static final int OPTION_REFERENCED = 1;
|
||||
public static final int OPTION_PART_OF = 2;
|
||||
public static final int OPTION_LAZY = 4;
|
||||
public static final int OPTION_WEAK = 8;
|
||||
public static final int OPTION_COLLECTION = 16; //Whether the value is a collection type.//
|
||||
public static final int OPTION_MAPPED = 32; //Whether the collection is mapped via a lookup table and binding class. This requires the collection's adds and removes to be added to the mapping.//
|
||||
public static final int OPTION_TRANSIENT = 64;
|
||||
public static final int OPTION_NO_REFLECT = 128;
|
||||
public static final int OPTION_CALCULATED = 256;
|
||||
public static final int OPTION_REFLECT_AS_IMMUTABLE = 512; //Treats the reference as if it pointed to an immutable object when reflecting. Immutable objects are not themselves reflected, the reflection mearly references the same object as the reflected object does.//
|
||||
public static final int OPTION_KEY = 1024;
|
||||
|
||||
public static final Object DEFAULT_VALUE_NULL = new Object();
|
||||
|
||||
/** The mapping of repository metadata for this attribute indexed by the application defined repository identifier. */
|
||||
private LiteHashMap attributeRepositoryMetadataMap = new LiteHashMap(2);
|
||||
/** TODO: Determine if this is ever going to be used again. This was the attribute's type, but there is no easy way to specify it in the metadata. */
|
||||
private Class type = null;
|
||||
/** The options selected in the attribute's definition. */
|
||||
private int options = OPTION_REFERENCED;
|
||||
/** The attribute related to this attribute, or -1 if the attribute's value is not bound to another attribute's value. This must be set to an attribute number in the same class or subclass if the attribute is bound. */
|
||||
private int relatedAttribute = -1;
|
||||
/** The attribute within the related attribute's value to which this attribute's value is bound, or -1 if this attribute is not bound to another. If bound, this attribute's value will always be the same as the value of the bound attribute. */
|
||||
private int boundAttribute = -1;
|
||||
/** The class of object that must reference this object as part-of in order for this attribute to be assigned a value. The value will be the bound attribute's value on the referencing object, or will be the referencing object if the bound attribute is -1. */
|
||||
private Class boundType = null;
|
||||
/** The default value that should be used for the attribute's value is none is provided. This should not be used if the option LAZY is used. This will be null if a default value is not given. This will be DEFAULT_VALUE_NULL if the default value is null. */
|
||||
private Object defaultValue = null;
|
||||
/** The optional class of object referenced by the attribute. 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. */
|
||||
private Class valueType = null;
|
||||
/**
|
||||
* AttributeMetadata constructor.
|
||||
* @param declaringType The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param options The options associated with the attribute.
|
||||
* @param valueType The optional class of object referenced by the attribute. 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.
|
||||
*/
|
||||
public AttributeMetadata(Class declaringType, String name, int options, Class valueType) {
|
||||
super(declaringType, name);
|
||||
|
||||
setName(name);
|
||||
setOptions(options);
|
||||
setValueType(valueType);
|
||||
}//AttributeMetadata()//
|
||||
/**
|
||||
* AttributeMetadata constructor.
|
||||
* @param declaringType The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param relatedAttribute The attribute number that this attribute is related to.
|
||||
* @param boundAttribute The attribute number on the related attribute's value to which the value of this attribute is bound to.
|
||||
*/
|
||||
public AttributeMetadata(Class declaringType, String name, int relatedAttribute, int boundAttribute) {
|
||||
super(declaringType, name);
|
||||
|
||||
setName(name);
|
||||
this.relatedAttribute = relatedAttribute;
|
||||
this.boundAttribute = boundAttribute;
|
||||
}//AttributeMetadata()//
|
||||
/**
|
||||
* AttributeMetadata constructor.
|
||||
* @param declaringType The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param boundType The the referencing class (part-of references only) that will supply the value for this attribute. The value will be the referencing class its self.
|
||||
*/
|
||||
public AttributeMetadata(Class declaringType, String name, Class boundType) {
|
||||
super(declaringType, name);
|
||||
|
||||
setName(name);
|
||||
this.boundType = boundType;
|
||||
}//AttributeMetadata()//
|
||||
/**
|
||||
* Gets the optional class of object referenced by the attribute. 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 optional attribute's referenced value's type.
|
||||
*/
|
||||
public Class getValueType() {
|
||||
return valueType;
|
||||
}//getValueType()//
|
||||
/**
|
||||
* Sets the optional class of object referenced by the attribute. 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 valueType The optional attribute's referenced value's type.
|
||||
*/
|
||||
private void setValueType(Class valueType) {
|
||||
this.valueType = valueType;
|
||||
}//setValueType()//
|
||||
/**
|
||||
* Adds an attribute as defined in the repository.
|
||||
* @param repositoryIdentifier Identifier for the repository associated with the repository attribute metadata.
|
||||
* @param attribute The attribute metadata for the repository.
|
||||
*/
|
||||
void addAttributeRepositoryMetadata(Object repositoryIdentifier, AttributeRepositoryMetadata attribute) {
|
||||
attributeRepositoryMetadataMap.put(repositoryIdentifier, attribute);
|
||||
}//addAttributeRepositoryMetadata()//
|
||||
/**
|
||||
* Gets an attribute as defined in the repository.
|
||||
* @param repositoryIdentifier The identifier for the repository whose attribute metadata is desired.
|
||||
* @return The attribute repository metadata for the given attribute. If the attribute does not have repository metadata then <code>null</code> will be returned.
|
||||
*/
|
||||
public AttributeRepositoryMetadata getAttributeRepositoryMetadata(Object repositoryIdentifier) {
|
||||
return (AttributeRepositoryMetadata) attributeRepositoryMetadataMap.get(repositoryIdentifier);
|
||||
}//getAttributeRepositoryMetadata()//
|
||||
/**
|
||||
* Gets attribute number used to get the bound attribute, or -1 if this attribute is not bound to the value of another attribute.
|
||||
* If provided, this attributes value will always equal the related attribute's bound attribute value.
|
||||
* <p>Warning: This number may not stay the same from one run of the application to the next. Do not rely on it being the same number for every run or every process.</p>
|
||||
* @return The number of the attribute related to this attribute and whose value is used to get the value for this attribute (via the bound attribute number).
|
||||
* @see #getBoundAttribute()
|
||||
*/
|
||||
public int getRelatedAttribute() {
|
||||
return relatedAttribute;
|
||||
}//getRelatedAttribute()//
|
||||
/**
|
||||
* Gets attribute number whose value is identical to this attribute's value, or -1 if this attribute is not bound to the value of another attribute.
|
||||
* If provided, this attributes value will always equal the related attribute's bound attribute value.
|
||||
* <p>Warning: This number may not stay the same from one run of the application to the next. Do not rely on it being the same number for every run or every process.</p>
|
||||
* @return The number of the attribute bound to this attribute and whose value is the value for this attribute.
|
||||
* @see #getRelatedAttribute()
|
||||
*/
|
||||
public int getBoundAttribute() {
|
||||
return boundAttribute;
|
||||
}//getBoundAttribute()//
|
||||
/**
|
||||
* Gets the class of object that must reference this object as part-of in order for this attribute to be assigned a value. The attributes value will be the referencing object if this is non-null.
|
||||
* @return The part-of referencing object's expected class.
|
||||
*/
|
||||
public Class getBoundType() {
|
||||
return boundType;
|
||||
}//getBoundType()//
|
||||
/**
|
||||
* Gets attribute identifier which encapsulates the number in the model.
|
||||
* @return The object representing the attribute in the model.
|
||||
*/
|
||||
public Attribute getAttributeIdentifier() {
|
||||
return (Attribute) getEventIdentifier();
|
||||
}//getAttributeIdentifier()//
|
||||
/**
|
||||
* Gets the attribute's options.
|
||||
* @return The options that describe the attribute and how it should be treated.
|
||||
*/
|
||||
int getOptions() {
|
||||
return options;
|
||||
}//getOptions()//
|
||||
/**
|
||||
* Gets the attribute's type.
|
||||
* @return The class which describes the object values for this attribute.
|
||||
*/
|
||||
public Class getType() {
|
||||
return type;
|
||||
}//getType()//
|
||||
/**
|
||||
* Gets the attribute's default value.
|
||||
* <p>The caller must first call hasDefaultValue() to determine if the default value should be used. This method may return null if the default value is to be null or if no default value is given.</p>
|
||||
* @return The default value for the attribute.
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
if(defaultValue == null) {
|
||||
//Cannot call this method if there isn't a default value assigned. This exception is to catch application design errors.//
|
||||
throw new RuntimeException();
|
||||
}//if//
|
||||
|
||||
return defaultValue == DEFAULT_VALUE_NULL ? null : defaultValue;
|
||||
}//getDefaultValue()//
|
||||
/**
|
||||
* Determines whether the attribute has been assigned a default value.
|
||||
* @return Whether there is a valid default value for this attribute.
|
||||
*/
|
||||
public boolean hasDefaultValue() {
|
||||
return defaultValue != null;
|
||||
}//hasDefaultValue()//
|
||||
/**
|
||||
* Determines whether the attribute is bound to another attribute.
|
||||
* @return Whether the attribute is bound to another attribute for its value. For example if Address has a CONTACT attribute and Contact has an ID attribute and Address then defines a CONTACT_ID attribute that is bound to the CONTACT attribute's ID attribute.
|
||||
*/
|
||||
public boolean isAttributeBound() {
|
||||
return boundAttribute != -1 && relatedAttribute != -1;
|
||||
}//isAttributeBound()//
|
||||
/**
|
||||
* Determines whether the attribute is a collection attribute.
|
||||
* @return Whether the attribute value is a collection type.
|
||||
*/
|
||||
public boolean isCollection() {
|
||||
return (options & OPTION_COLLECTION) != 0;
|
||||
}//isCollection()//
|
||||
/**
|
||||
* Determines whether the attribute is lazily initialized.
|
||||
* @return Whether lazy initialization is required for this attribute.
|
||||
*/
|
||||
public boolean isLazy() {
|
||||
return (options & OPTION_LAZY) != 0;
|
||||
}//isLazy()//
|
||||
/**
|
||||
* Determines whether the attribute is a collection attribute that requires adds and removes to be recorded using a mapping table (for N to N relationships).
|
||||
* @return Whether the attribute collection of references is mapped via a mapping table and associated class.
|
||||
*/
|
||||
public boolean isMapped() {
|
||||
return (options & OPTION_MAPPED) != 0;
|
||||
}//isMapped()//
|
||||
/**
|
||||
* Determines whether the attribute value is part of the type and thus shares a synchronization monitor.
|
||||
* @return Whether the value is part of which means it only exists within the context of the value's referencing object (this type).
|
||||
*/
|
||||
public boolean isPartOf() {
|
||||
return (options & OPTION_PART_OF) != 0;
|
||||
}//isPartOf()//
|
||||
/**
|
||||
* Determines whether the attribute value is referenced (versus contained).
|
||||
* @return Whether the value is referenced and it exists independantly of the attribute's object.
|
||||
*/
|
||||
public boolean isReferenced() {
|
||||
return (options & OPTION_REFERENCED) != 0;
|
||||
}//isReferenced()//
|
||||
/**
|
||||
* Determines whether the attribute value is weakly referenced.
|
||||
* @return Whether the value wrappered by a weak reference within the attribute subsystem. Normally this should only be true if the attribute has a lazy load handler.
|
||||
*/
|
||||
public boolean isWeak() {
|
||||
return (options & OPTION_WEAK) != 0;
|
||||
}//isWeak()//
|
||||
/**
|
||||
* Determines whether the attribute value is not serializable.
|
||||
* @return Whether the value is transient, meaning it does not get serialized automatically with the rest of the attributes.
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
return (options & OPTION_TRANSIENT) != 0;
|
||||
}//isTransient()//
|
||||
/**
|
||||
* 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).
|
||||
* Note: Currently calculated attributes should not be reflected since the reflections will need to calculate it themselves.
|
||||
* @return Whether the attribute is to be copied or reflected along with the other attributes when the containing object is reflected.
|
||||
*/
|
||||
public boolean isReflectable() {
|
||||
//Note: Not reflecting calculated attributes used to be the solution for forcing recalculation as the algorithms may change. Setting the db field to null is a better solution however since not reflecting the value causes performance problems and doesn't affect the db.//
|
||||
return ((options & OPTION_NO_REFLECT) == 0)/* && ((options & OPTION_CALCULATED) == 0)*/;
|
||||
}//isReflectable()//
|
||||
/**
|
||||
* Determines whether the attribute value is reflected as if it were referencing an immutable value.
|
||||
* @return Whether the attribute is to be referenced instead of reflected by the reflection (immutable values are always referenced by the reflection unless marked as non-reflectable).
|
||||
*/
|
||||
public boolean isReflectAsImmutable() {
|
||||
return ((options & OPTION_REFLECT_AS_IMMUTABLE) != 0);
|
||||
}//isReflectAsImmutable()//
|
||||
/**
|
||||
* Determines whether the attribute value is based on other attribute values and must be recalculated when certain other attributes change their values.
|
||||
* @return Whether the attribute's value is based on the values of other attributes.
|
||||
*/
|
||||
public boolean isCalculated() {
|
||||
return (options & OPTION_CALCULATED) != 0;
|
||||
}//isCalculated()//
|
||||
/**
|
||||
* Determines whether the attribute is a back reference, meaning its value is set when another object references the defining class instance as part-of.
|
||||
* @return Whether the attribute's value is based on the part-of reference to the object.
|
||||
*/
|
||||
public boolean isBackReference() {
|
||||
return boundType != null;
|
||||
}//isBackReference()//
|
||||
/**
|
||||
* Determines whether the attribute is part of the key.
|
||||
* @return Whether the attribute's value is included in the key.
|
||||
*/
|
||||
public boolean isKey() {
|
||||
return (options & OPTION_KEY) != 0;
|
||||
}//isKey()//
|
||||
/**
|
||||
* Sets the attribute's options.
|
||||
* @param options The options that describe the attribute and how it should be treated.
|
||||
*/
|
||||
void setOptions(int options) {
|
||||
this.options = options;
|
||||
}//setOptions()//
|
||||
/**
|
||||
* Sets the attribute's type.
|
||||
* @param type The class which describes the object values for this attribute.
|
||||
*/
|
||||
void setType(Class type) {
|
||||
this.type = type;
|
||||
}//setType()//
|
||||
/**
|
||||
* Sets the default value for the attribute. The default value is initialized to not-set (ie the attribute is not assigned a default value).
|
||||
* @param defaultValue The default value which can be null or any immutable object (or any object that will be treated by the app as immutable).
|
||||
*/
|
||||
void setDefaultValue(Object defaultValue) {
|
||||
this.options |= OPTION_LAZY;
|
||||
this.defaultValue = defaultValue;
|
||||
}//setDefaultValue()//
|
||||
/**
|
||||
* Gets the debug string for the metadata.
|
||||
* @return A human readable representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
return getName() != null ? getName() : ("" + getNumber());
|
||||
}//toString()//
|
||||
}//AttributeMetadata//
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.metadata;
|
||||
|
||||
import com.common.util.*;
|
||||
|
||||
/*
|
||||
* The metadata for an attribute of a type for a repository.
|
||||
*/
|
||||
public class AttributeRepositoryMetadata {
|
||||
/** The type repository metadata for the attribute this object is associated with. */
|
||||
private TypeRepositoryMetadata typeRepositoryMetadata = null;
|
||||
/** The attribute metadata for the attribute this object is associated with. */
|
||||
private AttributeMetadata attributeMetadata = null;
|
||||
/** The name of the attribute in the repository. In a RDB this would be the column name. */
|
||||
private String name = null;
|
||||
/** The attribute's internal type code (as expected by the java class that has this attribute). If this is non-null, and not the same as the external type, then the transaction framework will convert the value to/from the respository type. */
|
||||
private Integer internalTypeCode = null;
|
||||
/** The attribute's type in the repository. */
|
||||
private Object repositoryType = null;
|
||||
/** Properties specific to this attribute. These properties should be defined as identifiers in the resource manager class. */
|
||||
private LiteHashMap properties = new LiteHashMap(20);
|
||||
/** Whether this attribute's value is automatically generated in the repository when the object is inserted in the repository. */
|
||||
private boolean isAutoGenerated = false;
|
||||
/** Whether this attribute helps uniquely identify the object in the repository. */
|
||||
private boolean isKey = false;
|
||||
/** Whether the attribute is the logical object id attribute in the repository. */
|
||||
private boolean isLogicalObjectId = false;
|
||||
/** Whether the attribute is the version attribute in the repository. */
|
||||
private boolean isVersion = false;
|
||||
/** The optional repository class name (table in a relational db). This can be null if the type repository metadata's repository class name is to be used. */
|
||||
private String className = null;
|
||||
/** The collection of join metadata. */
|
||||
private LiteList joins;
|
||||
/** Whether this attribute's value is automatically generated in the repository when the object updates the repository. */
|
||||
private boolean isAutoUpdated = false;
|
||||
/** The optional name used by the repository to report the new value for the attribute after an update. This can prevent a secondary query from going to the repository to discover the value for single row updates. */
|
||||
private String autoUpdateResultName = null;
|
||||
/** The sequence name required by some drivers to retrieve the sequence value after it is used to auto-update a cell in the table during an insert or update query. Generally these values are returned via the query results, but some db's don't support that feature and require a secondary query. */
|
||||
private String sequenceName = null;
|
||||
|
||||
/**
|
||||
* Encapsulates the join information for an attribute.
|
||||
*/
|
||||
public static class Join {
|
||||
/** The name of the attribute in the repository. In a RDB this would be the column name. */
|
||||
private String name = null;
|
||||
/** The repository class name (table in a relational db). */
|
||||
private String className = null;
|
||||
|
||||
/**
|
||||
* Join constructor.
|
||||
* @param name The name of the attribute in the repository. In a RDB this would be the column name.
|
||||
* @param className The optional repository class name (table in a relational db). This can be null if the type repository metadata's repository class name is to be used. This must not conflict with any other join or with the attribute's repository name.
|
||||
*/
|
||||
public Join(String name, String className) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
}//Join()//
|
||||
/**
|
||||
* Gets the name of the attribute (a column name in a relational DB) in the repository.
|
||||
* @return The repository name for this attribute.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the class name (a table name in a relational db) for the attribute in the repository.
|
||||
* @return The repository class that the attribute is defined in. An attribute can be specified more than once within a type and repository if the class name differs in which case it will be assumed that the attribute is to be used in a join.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}//getClassName()//
|
||||
}//Join//
|
||||
/**
|
||||
* AttributeRepositoryMetadata constructor.
|
||||
*/
|
||||
protected AttributeRepositoryMetadata() {
|
||||
super();
|
||||
}//AttributeRepositoryMetadata()//
|
||||
/**
|
||||
* AttributeRepositoryMetadata constructor.
|
||||
* @param joins A collection of Join instances containing all combinations of repository class (table in an RDB) and repository attribute (column in an RDB). This will be null if the attribute isn't used as a join in the repository.
|
||||
*/
|
||||
public AttributeRepositoryMetadata(TypeRepositoryMetadata typeRepositoryMetadata, AttributeMetadata attributeMetadata, String name, String className, Integer internalTypeCode, Object repositoryType, LiteHashMap properties, boolean isKey, boolean isAutoGenenerated, boolean isLogicalObjectId, boolean isVersion, IList joins, boolean isAutoUpdated, String autoUpdateResultName, String sequenceName) {
|
||||
super();
|
||||
|
||||
this.attributeMetadata = attributeMetadata;
|
||||
this.typeRepositoryMetadata = typeRepositoryMetadata;
|
||||
setName(name);
|
||||
setInternalTypeCode(internalTypeCode);
|
||||
setRepositoryType(repositoryType);
|
||||
setProperties(properties);
|
||||
setIsKey(isKey);
|
||||
setIsAutoGenerated(isAutoGenenerated);
|
||||
this.isVersion = isVersion;
|
||||
this.isLogicalObjectId = isLogicalObjectId;
|
||||
this.className = className;
|
||||
this.joins = joins == null ? LiteList.EMPTY_LIST : new LiteList(joins);
|
||||
this.isAutoUpdated = isAutoUpdated;
|
||||
this.sequenceName = sequenceName;
|
||||
|
||||
if(isAutoUpdated) {
|
||||
this.autoUpdateResultName = autoUpdateResultName;
|
||||
}//if//
|
||||
}//AttributeRepositoryMetadata()//
|
||||
/**
|
||||
* Gets the non-null collection of Join instances containing all combinations of repository class (table in an RDB) and repository attribute (column in an RDB).
|
||||
* @return The collection of Join instances, one for each join used by the repository to create or store the object in the repository. This will be an empty collection if the attribute doesn't join two or more repository classes.
|
||||
*/
|
||||
public IList getJoins() {
|
||||
return joins;
|
||||
}//getJoins()//
|
||||
/**
|
||||
* Gets the metadata for the attribute this object is associated with.
|
||||
* @return The attribute metadata for the attribute associated with this attribute repository metadata object.
|
||||
*/
|
||||
public AttributeMetadata getAttributeMetadata() {
|
||||
return attributeMetadata;
|
||||
}//getAttributeMetadata()//
|
||||
/**
|
||||
* Gets the type repository metadata for the repository type this object is associated with.
|
||||
* @return The type repository metadata the attribute belongs to.
|
||||
*/
|
||||
public TypeRepositoryMetadata getTypeRepositoryMetadata() {
|
||||
return typeRepositoryMetadata;
|
||||
}//getTypeRepositoryMetadata()//
|
||||
/**
|
||||
* Gets the attribute's internal type code (as expected by the java class that has this attribute). If this is non-null, and not the same as the external type, then the transaction framework will convert the value to/from the respository type.
|
||||
* @return The java class' attribute type code for this attribute.
|
||||
*/
|
||||
public Integer getInternalTypeCode() {
|
||||
return internalTypeCode;
|
||||
}//getInternalTypeCode()//
|
||||
/**
|
||||
* Determines whether this attribute's value is automatically generated when inserting an object in the repository.
|
||||
* @return Whether the value for the attribute will/can be automatically generated within the repository.
|
||||
*/
|
||||
public boolean getIsAutoGenerated() {
|
||||
return isAutoGenerated;
|
||||
}//getIsAutoGenerated()//
|
||||
/**
|
||||
* Determines whether this attribute helps uniquely identify the object in the repository.
|
||||
* @return Whether the value is a part of the object's identifying key.
|
||||
*/
|
||||
public boolean getIsKey() {
|
||||
return isKey;
|
||||
}//getIsKey()//
|
||||
/**
|
||||
* Determines whether this attribute is the logical object identifier in the repository.
|
||||
* @return Whether this attribute is the mapping to a logical object class (table in a relation db) identifier for the logical object in the repository. If one is supplied then the transaction system will treat the attribute specially.
|
||||
*/
|
||||
public boolean getIsLogicalObjectId() {
|
||||
return isLogicalObjectId;
|
||||
}//getIsLogicalObjectId()//
|
||||
/**
|
||||
* Determines whether this attribute is the version in the repository.
|
||||
* @return Whether the value contains the version number for the logical object in the repository. If one is supplied then the transaction system will treat the attribute specially.
|
||||
*/
|
||||
public boolean getIsVersion() {
|
||||
return isVersion;
|
||||
}//getIsVersion()//
|
||||
/**
|
||||
* Gets the class name (a table name in a relational db) for the attribute in the repository.
|
||||
* @return The repository class that the attribute is defined in.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return className != null ? className : getTypeRepositoryMetadata().getRepositoryClassName();
|
||||
}//getClassName()//
|
||||
/**
|
||||
* Gets the name of the attribute in the repository.
|
||||
* @return The repository name for this attribute. This is going to be the column name in a RDB repository.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the attribute's unique number that is assigned at runtime. This number should not be relied upon to stay the same from run to run of the application.
|
||||
* @return The attribute number for this run of the application.
|
||||
*/
|
||||
public int getNumber() {
|
||||
return getAttributeMetadata().getNumber();
|
||||
}//getNumber()//
|
||||
/**
|
||||
* Gets the attribute's properties.
|
||||
* @return The attribute's custom properties specific to the repository.
|
||||
*/
|
||||
public LiteHashMap getProperties() {
|
||||
return properties;
|
||||
}//getProperties()//
|
||||
/**
|
||||
* Gets the attribute's repository type.
|
||||
* @return The repository attribute type for this attribute. This is going to be the column type (as returned by the driver) in a RDB repository.
|
||||
*/
|
||||
public Object getRepositoryType() {
|
||||
return repositoryType;
|
||||
}//getRepositoryType()//
|
||||
/**
|
||||
* Sets the metadata for the attribute this object is associated with.
|
||||
* @param attributeMetadata The attribute metadata for the attribute associated with this attribute repository metadata object.
|
||||
*/
|
||||
void setAttributeMetadata(AttributeMetadata attributeMetadata) {
|
||||
this.attributeMetadata = attributeMetadata;
|
||||
}//setAttributeMetadata()//
|
||||
/**
|
||||
* Sets the attribute's internal type (as expected by the java class that has this attribute). If this is non-null, and not the same as the external type, then the transaction framework will convert the value to/from the respository type.
|
||||
* @param internalTypeCode The java class' attribute type code for this attribute.
|
||||
*/
|
||||
void setInternalTypeCode(Integer internalTypeCode) {
|
||||
this.internalTypeCode = internalTypeCode;
|
||||
}//setInternalTypeCode()//
|
||||
/**
|
||||
* Determines whether this attribute's value is automatically generated when inserting an object in the repository.
|
||||
* @param isAutoNumber Whether the value for the attribute will/can be automatically generated within the repository.
|
||||
*/
|
||||
void setIsAutoGenerated(boolean isAutoGenerated) {
|
||||
this.isAutoGenerated = isAutoGenerated;
|
||||
}//setIsAutoGenerated()//
|
||||
/**
|
||||
* Determines whether this attribute helps uniquely identify the object in the repository.
|
||||
* @param isKey Whether the value is a part of the object's identifying key.
|
||||
*/
|
||||
void setIsKey(boolean isKey) {
|
||||
this.isKey = isKey;
|
||||
}//setIsKey()//
|
||||
/**
|
||||
* Sets the name of the attribute in the repository.
|
||||
* @param name The repository name for this attribute. This is going to be the column name in a RDB repository.
|
||||
*/
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}//setName()//
|
||||
/**
|
||||
* Gets the attribute's properties.
|
||||
* @return The attribute's custom properties specific to the repository.
|
||||
*/
|
||||
void setProperties(LiteHashMap properties) {
|
||||
this.properties = properties;
|
||||
}//setProperties()//
|
||||
/**
|
||||
* Sets the attribute's repository type.
|
||||
* @param externalType The repository attribute type for this attribute. This is going to be the column type (as returned by the driver) in a RDB repository.
|
||||
*/
|
||||
void setRepositoryType(Object repositoryType) {
|
||||
this.repositoryType = repositoryType;
|
||||
}//setRepositoryType()//
|
||||
/**
|
||||
* Determines whether this attribute's value is automatically generated when the object is updated in the repository.
|
||||
* @return Whether the value is generated by the repository upon updating the object.
|
||||
*/
|
||||
public boolean getIsAutoUpdated() {
|
||||
return isAutoUpdated;
|
||||
}//getIsAutoUpdated()//
|
||||
/**
|
||||
* Determines whether this attribute's value is automatically generated when the object is updated in the repository.
|
||||
* @param isAutoNumber Whether the value is generated by the repository upon updating the object.
|
||||
*/
|
||||
void setIsAutoUpdated(boolean isAutoUpdated) {
|
||||
this.isAutoUpdated = isAutoUpdated;
|
||||
}//setIsAutoUpdated()//
|
||||
/**
|
||||
* Gets the name used by the repository to push the automatically generated value back to the application after an update query.
|
||||
* @return The optional name used by the repository to report the new value after an update query.
|
||||
*/
|
||||
public String getAutoUpdateResultName() {
|
||||
return autoUpdateResultName;
|
||||
}//getAutoUpdateResultName()//
|
||||
/**
|
||||
* Sets the name used by the repository to push the automatically generated value back to the application after an update query.
|
||||
* @param autoUpdateResultName The optional name used by the repository to report the new value after an update query.
|
||||
*/
|
||||
void setAutoUpdateResultName(String autoUpdateResultName) {
|
||||
this.autoUpdateResultName = autoUpdateResultName;
|
||||
}//setAutoUpdateResultName()//
|
||||
/**
|
||||
* Gets the sequence name required by the db to query for the result of an automaticly set value during an insert or update.
|
||||
* @return The name of the sequence required by some databases to retrieve an auto-generated value post update/insert.
|
||||
*/
|
||||
public String getSequenceName() {
|
||||
return sequenceName;
|
||||
}//getSequenceName()//
|
||||
/**
|
||||
* Sets the sequence name required by the db to query for the result of an automaticly set value during an insert or update.
|
||||
* @param sequenceName The name of the sequence required by some databases to retrieve an auto-generated value post update/insert.
|
||||
*/
|
||||
void setSequenceName(String sequenceName) {
|
||||
this.sequenceName = sequenceName;
|
||||
}//setSequenceName()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return getName() + ' ' + super.toString();
|
||||
}//toString()//
|
||||
}//AttributeRepositoryMetadata//
|
||||
77
Foundation/src/com/foundation/metadata/CloneContext.java
Normal file
77
Foundation/src/com/foundation/metadata/CloneContext.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.metadata;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/**
|
||||
* A context for a cloning operation. This maintains data needed by different parts of the clone operation.
|
||||
* A clone that is semi-shallow (only part of references are followed by the clone operation) and produces a meta-object clone should use this.
|
||||
*/
|
||||
public class CloneContext extends Object {
|
||||
/** The mapping of clone by cloned object. This is necessary since some cloned objects reference objects not as part-of, but the same object is referenced as part-of elsewhere in the meta-object. */
|
||||
private LiteHashMap cloneMap = new LiteHashMap(10, Comparator.getIdentityComparator(), Comparator.getIdentityComparator());
|
||||
/** A collection of listeners for the cleanup event. Cleanup occurs after all the clones have been created. */
|
||||
private LiteList cleanupListeners = new LiteList(10);
|
||||
|
||||
public static interface IPostCloneCleanupListener {
|
||||
/**
|
||||
* Called after the cloning process is completed but before the clone is made public.
|
||||
* This allows a cloned component an opportunity to replace any non-cloned object references with clones if they were cloned by a different part of the cloning process.
|
||||
* @param context The cloning context object which provides access to the clones indexed by the cloned object references.
|
||||
*/
|
||||
public void postCloneCleanup(CloneContext context);
|
||||
}//IPostCloneCleanupListener//
|
||||
/**
|
||||
* CloneContext constructor.
|
||||
*/
|
||||
public CloneContext() {
|
||||
super();
|
||||
}//CloneContext()//
|
||||
/**
|
||||
* Gets the clone for a given non-clone value.
|
||||
* @param instance The non-clone reference.
|
||||
* @return The clone or null.
|
||||
*/
|
||||
public Object getClone(Object instance) {
|
||||
return cloneMap.get(instance);
|
||||
}//getClone()//
|
||||
/**
|
||||
* Sets the clone for a given non-clone value.
|
||||
* @param instance The non-clone reference.
|
||||
* @param clone The clone.
|
||||
*/
|
||||
public void setClone(Object instance, Object clone) {
|
||||
cloneMap.put(instance, clone);
|
||||
}//setClone()//
|
||||
/**
|
||||
* Adds a listener that will be called after the cloning process is completed but before the clone is returned.
|
||||
* @param listener The listener to be notified to allow for post clone cleanup (such as replacing non-cloned references with cloned references where applicable).
|
||||
*/
|
||||
public void addPostCloneCleanupListener(IPostCloneCleanupListener listener) {
|
||||
cleanupListeners.add(listener);
|
||||
}//addPostCloneCleanupListener()//
|
||||
/**
|
||||
* Releases the clone context and calls any post clone cleanup listeners.
|
||||
*/
|
||||
public void release() {
|
||||
//TODO: Would help to make the map immutable.
|
||||
|
||||
//Notify the cleanup listeners.//
|
||||
for(int index = 0; index < cleanupListeners.getSize(); index++) {
|
||||
((IPostCloneCleanupListener) cleanupListeners.get(index)).postCloneCleanup(this);
|
||||
}//for//
|
||||
|
||||
cleanupListeners.removeAll();
|
||||
cloneMap.removeAll();
|
||||
cloneMap = null;
|
||||
cleanupListeners = null;
|
||||
}//release()//
|
||||
}//CloneContext//
|
||||
64
Foundation/src/com/foundation/metadata/Event.java
Normal file
64
Foundation/src/com/foundation/metadata/Event.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 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.metadata;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
public class Event implements IExternalizable {
|
||||
/** The metadata for the event. */
|
||||
private EventMetadata metadata = null;
|
||||
/**
|
||||
* Converts an array of events (or subclasses) into an array of identifiers.
|
||||
* @param events The events.
|
||||
* @return The identifiers.
|
||||
*/
|
||||
public static int[] convert(Event[] events) {
|
||||
int[] result = new int[events.length];
|
||||
|
||||
for(int index = events.length - 1; index >= 0; index--) {
|
||||
result[index] = events[index].getNumber();
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//convert()//
|
||||
/**
|
||||
* Event constructor.
|
||||
*/
|
||||
protected Event(EventMetadata metadata) {
|
||||
super();
|
||||
this.metadata = metadata;
|
||||
}//Event()//
|
||||
/**
|
||||
* Gets the unique number identifying the event.
|
||||
* @return The event's unique number. This may be a global number if the event is defined by an interface.
|
||||
*/
|
||||
public int getNumber() {
|
||||
return metadata.getNumber();
|
||||
}//getNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
int declaredNumber = in.readInt();
|
||||
Class type = (Class) in.readObject();
|
||||
TypeMetadata typeMetadata = type != null ? MetadataService.getSingleton().getTypeMetadata(type) : null;
|
||||
|
||||
return typeMetadata != null ? typeMetadata.getDeclaredEventMetadata(declaredNumber).getEventIdentifier() : IExternalizable.NULL_VALUE;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(metadata.getDeclaredNumber());
|
||||
out.writeObject(metadata.getDeclaringType());
|
||||
}//writeExternal()//
|
||||
}//Event//
|
||||
69
Foundation/src/com/foundation/metadata/EventMetadata.java
Normal file
69
Foundation/src/com/foundation/metadata/EventMetadata.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2007 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.metadata;
|
||||
|
||||
/**
|
||||
* This class manages the metadata for an event of a class in the software system.
|
||||
*/
|
||||
public class EventMetadata extends AbstractMetadata {
|
||||
/** The first number in the custom event number sequence. This is used to offset event numbers to play nice with attribute numbers (which double as event numbers). */
|
||||
public static final int CUSTOM_EVENT_NUMBER_OFFSET = 0x0000FFFF;
|
||||
/** The first number in the global event number sequence. This is used to map global event numbers to local event numbers for classes implementing interfaces defining events. Note that this sequence is on the range [2^-31..-1]. */
|
||||
public static final int GLOBAL_EVENT_NUMBER_OFFSET = Integer.MIN_VALUE + 1; //Note: Add one since some int hashmaps use the lowest integer value as the invalid key.//
|
||||
|
||||
/** The class which declared this event metadata. */
|
||||
private Class declaringType = null;
|
||||
/** The event identifier that encapsulates the event number. */
|
||||
private Event identifier = null;
|
||||
/**
|
||||
* EventMetadata constructor.
|
||||
* @param declaringType The class which has declared this event.
|
||||
* @param name The name of the event.
|
||||
*/
|
||||
public EventMetadata(Class declaringType, String name) {
|
||||
super();
|
||||
|
||||
setDeclaringType(declaringType);
|
||||
setName(name);
|
||||
}//EventMetadata()//
|
||||
/**
|
||||
* Gets the class that declared the event.
|
||||
* @return The class which declared this event.
|
||||
*/
|
||||
public Class getDeclaringType() {
|
||||
return declaringType;
|
||||
}//getDeclaringType()//
|
||||
/**
|
||||
* Gets event identifier which encapsulates the number in the model.
|
||||
* @return The object representing the event in the model.
|
||||
*/
|
||||
public Event getEventIdentifier() {
|
||||
return identifier;
|
||||
}//getEventIdentifier()//
|
||||
/**
|
||||
* Sets the class that declared the event.
|
||||
* @param declaringType The class which declared this event.
|
||||
*/
|
||||
private void setDeclaringType(Class declaringType) {
|
||||
this.declaringType = declaringType;
|
||||
}//setDeclaringType()//
|
||||
/**
|
||||
* Sets event identifier which encapsulates the number in the model.
|
||||
* @param identifier The object representing the event in the model.
|
||||
*/
|
||||
void setIdentifier(Event identifier) {
|
||||
this.identifier = identifier;
|
||||
}//setIdentifier()//
|
||||
/**
|
||||
* Gets the debug string for the metadata.
|
||||
* @return A human readable representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
return getName() != null ? getName() : ("" + getNumber());
|
||||
}//toString()//
|
||||
}//EventMetadata//
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2006 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.metadata;
|
||||
|
||||
import com.common.thread.Monitor;
|
||||
import com.foundation.clone.ICloneable;
|
||||
|
||||
/**
|
||||
* An interface that should be implemented by any class that supplies a type callback handler and supplies metadata.
|
||||
* <p>TODO: Determine whether this interface is really needed. It used to be used to pass around containment references,
|
||||
* but changes were made to explicitly pass Entity references for containment instead. It may just be replaced by entity casts.</p>
|
||||
*/
|
||||
public interface ISupportsContainment extends ICloneable {
|
||||
/**
|
||||
* Gets the object that must be synchronized upon in order to make changes or access this entity.
|
||||
* @return The object that should be synchronized upon when a thread is accessing this entity.
|
||||
*/
|
||||
public Monitor getMonitor();
|
||||
}//ISupportsContainment//
|
||||
121
Foundation/src/com/foundation/metadata/InterfaceMetadata.java
Normal file
121
Foundation/src/com/foundation/metadata/InterfaceMetadata.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2007 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.metadata;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.IHashMap;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
public class InterfaceMetadata {
|
||||
/** Whether the interface has finished being setup. */
|
||||
private boolean isFinished = false;
|
||||
/** The collection of event metadata instances declared by this interface and indexed by their global event numbers. */
|
||||
private IList declaredEventList = null;
|
||||
/** The collection of event metadata instances indexed by their event numbers. */
|
||||
private IList eventList = null;
|
||||
/** The metadata for the super interfaces. */
|
||||
private InterfaceMetadata[] interfaceMetadata = null;
|
||||
/** The event metadata for this type indexed by event name. */
|
||||
private IHashMap eventMap = new LiteHashMap(5);
|
||||
/**
|
||||
* InterfaceMetadata constructor.
|
||||
* @param interfaceType The interface class.
|
||||
*/
|
||||
InterfaceMetadata(Class interfaceType, InterfaceMetadata[] interfaceMetadata) {
|
||||
super();
|
||||
this.interfaceMetadata = interfaceMetadata;
|
||||
this.declaredEventList = new LiteList(8, 10);
|
||||
|
||||
//Setup the event list and names to include all super interface events.//
|
||||
if((interfaceMetadata != null) && (interfaceMetadata.length > 0)) {
|
||||
int count = 0;
|
||||
|
||||
for(int index = 0; index < interfaceMetadata.length; index++) {
|
||||
count += interfaceMetadata[index].eventList.getSize();
|
||||
}//for//
|
||||
|
||||
if(count > 0) {
|
||||
this.eventList = new LiteList(count, 10);
|
||||
|
||||
for(int index = 0; index < interfaceMetadata.length; index++) {
|
||||
eventList.addAll(interfaceMetadata[index].eventList);
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
this.eventList = new LiteList(8, 10);
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
this.eventList = new LiteList(8, 10);
|
||||
}//else//
|
||||
}//InterfaceMetadata()//
|
||||
/**
|
||||
* Adds the global event metadata to this interface.
|
||||
* @param eventMetadata The metadata for the new event.
|
||||
*/
|
||||
protected void addEventMetadata(EventMetadata eventMetadata) {
|
||||
int hierarchyNumber = eventList.getSize();
|
||||
int declaredNumber = declaredEventList.getSize();
|
||||
EventMetadata duplicate = (EventMetadata) eventMap.put(eventMetadata.getName(), eventMetadata);
|
||||
|
||||
//Place the event in the named event map by the event name.//
|
||||
if((duplicate != null) && (duplicate != eventMetadata)) {
|
||||
Debug.log(new RuntimeException("Duplicate event named " + eventMetadata.getName() + " found in the types " + duplicate.getDeclaringType().getName() + " and " + eventMetadata.getDeclaringType().getName() + "."));
|
||||
}//if//
|
||||
else {
|
||||
eventMetadata.setDeclaredNumber(declaredNumber);
|
||||
eventMetadata.setHierarchyNumber(hierarchyNumber);
|
||||
eventList.add(eventMetadata);
|
||||
declaredEventList.add(eventMetadata);
|
||||
}//else//
|
||||
}//addEventMetadata()//
|
||||
/**
|
||||
* Gets the collection of event metadata declared by this interface.
|
||||
* @return The collection of event metadata instances declared by this interface and indexed by their global event numbers.
|
||||
*/
|
||||
IList getDeclaredEventList() {
|
||||
return declaredEventList;
|
||||
}//getDeclaredEventList()//
|
||||
/**
|
||||
* Gets the collection of event metadata declared by this interface and all super interfaces.
|
||||
* @return The collection of event metadata instances declared by this interface plus all inherited interfaces, and indexed by their global event numbers.
|
||||
*/
|
||||
IList getEventList() {
|
||||
return eventList;
|
||||
}//getDeclaredEventList()//
|
||||
/**
|
||||
* Allows the metadata to complete its initialization and to optimize for future use.
|
||||
*/
|
||||
void finish() {
|
||||
//Mark this metadata as finished.//
|
||||
isFinished = true;
|
||||
|
||||
if((interfaceMetadata != null) && (interfaceMetadata.length > 0)) {
|
||||
for(int index = 0; index < interfaceMetadata.length; index++) {
|
||||
interfaceMetadata[index].finish();
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//Complete the lists of declared events.//
|
||||
if(declaredEventList == null) {
|
||||
declaredEventList = LiteList.EMPTY_LIST;
|
||||
}//if//
|
||||
else {
|
||||
declaredEventList.isChangeable(false);
|
||||
}//else//
|
||||
}//finish()//
|
||||
/**
|
||||
* Determines whether the interface metadata has been 'finished'.
|
||||
* @return Whether the metadata has stopped accepting metadata changes.
|
||||
*/
|
||||
boolean isFinished() {
|
||||
return isFinished;
|
||||
}//isFinished()//
|
||||
}//InterfaceMetadata//
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2006,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.metadata;
|
||||
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.common.EntityMetadata;
|
||||
import com.foundation.common.MetadataContainer;
|
||||
import com.foundation.util.IInlineCollectionObserver;
|
||||
import com.foundation.attribute.AttributeSupport;
|
||||
|
||||
/**
|
||||
* Used to track what objects and listeners require cleanup after a call to load attributes for an entity or entity tree.
|
||||
* The loading of attributes may require loss of the loading entity's monitor lock which allows other threads to make changes to the entity tree.
|
||||
* To prevent this from disrupting the load attributes process which is usually a part of a larger process such as cloning or reflecting,
|
||||
* the load attributes process must track what objects require cleanup, and which objects require a call to load the object's attributes.
|
||||
* <p>
|
||||
* Note: This context is multi-thread safe when used properly, though no synchronization is necessary.
|
||||
* The user of the context must lock on the instance's monitor prior to creating the context and must not unlock until the context is released.
|
||||
* The loading of lazy attributes may cause the lock to be suspended, but the context will be listening for changes to the necessary attributes.
|
||||
* Change notification may occur on other threads, but that shouldn't be a problem since the other threads must hold the instance's monitor.
|
||||
* If this context is used to load attributes in a single threaded reflection context then other threads and locking are irrelevant and unncessary.
|
||||
* </p>
|
||||
*/
|
||||
public class LoadAttributesContext implements AttributeSupport.IAttributeChangeListener, IInlineCollectionObserver {
|
||||
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_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_EXCLUDE_STANDARD_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES;
|
||||
public static final int LOAD_ATTRIBUTE_OPTION_EXCLUDE_BOUND_ATTRIBUTES = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_EXCLUDE_BOUND_ATTRIBUTES;
|
||||
public static final int LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT = TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT;
|
||||
|
||||
/** A collection of objects which must be notified after the loading of the attributes is completed and this context is released. */
|
||||
private LiteHashSet processedObjects = new LiteHashSet(20);
|
||||
/** A collection of objects which must be checked for attributes to be loaded. */
|
||||
private LiteList pendingObjects = new LiteList(20, 100);
|
||||
/** Zero or more of the TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_XX flags. */
|
||||
private int options = 0;
|
||||
/** The container containing the metadata that guides what is loaded and what is traversed. If this is null then the options are used. */
|
||||
private MetadataContainer metadataContainer = null;
|
||||
/**
|
||||
* LoadAttributesContext constructor.
|
||||
* <p>Warning: The caller must hold the monitor lock for the instance parameter.</p>
|
||||
* @param instance The object whose attributes should be loaded. Depending on the metadata provided the loading of attributes may include other part-of objects.
|
||||
* @param metadataContainer The container containing the metadata that guides what is loaded and what is traversed.
|
||||
*/
|
||||
public LoadAttributesContext(Object instance, MetadataContainer metadataContainer) {
|
||||
super();
|
||||
this.metadataContainer = metadataContainer;
|
||||
this.options = LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT;
|
||||
loadAttributes(instance);
|
||||
}//LoadAttributesContext()//
|
||||
/**
|
||||
* LoadAttributesContext constructor.
|
||||
* <p>Warning: The caller must hold the monitor lock for the instance parameter.</p>
|
||||
* @param instance The object whose attributes should be loaded. Depending on the metadata provided the loading of attributes may include other part-of objects.
|
||||
* @param metadataContainer The container containing the metadata that guides what is loaded and what is traversed.
|
||||
* @param options Zero or more of the TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_XX flags, except for LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT which is ignored. If any options are provided then they will superceed the metadata. Exclude back references is implied.
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_PART_OF_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_CALCULATED_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_WEAK_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_COLLECTION_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES
|
||||
*/
|
||||
public LoadAttributesContext(Object instance, MetadataContainer metadataContainer, int options) {
|
||||
super();
|
||||
this.metadataContainer = metadataContainer;
|
||||
this.options = options | LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT;
|
||||
loadAttributes(instance);
|
||||
}//LoadAttributesContext()//
|
||||
/**
|
||||
* LoadAttributesContext constructor.
|
||||
* <p>Warning: The caller must hold the monitor lock for the instance parameter.</p>
|
||||
* @param instance The object whose attributes should be loaded. Depending on the options provided the loading of attributes may include other part-of objects.
|
||||
* @param options Zero or more of the TypeCallbackHandler.LOAD_ATTRIBUTE_OPTION_XX flags. Exclude back references is implied.
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_PART_OF_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_CALCULATED_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_WEAK_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_COLLECTION_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES
|
||||
* @see LOAD_ATTRIBUTE_OPTION_TRAVERSE_LOGICAL_OBJECT
|
||||
*/
|
||||
public LoadAttributesContext(Object instance, int options) {
|
||||
super();
|
||||
this.options = options;
|
||||
loadAttributes(instance);
|
||||
}//LoadAttributesContext()//
|
||||
/**
|
||||
* Determines whether the attribute is included based on the settings in this load attributes context and the entity metadata retrieved from this object.
|
||||
* @param attributeMetadata The metadata for the attribute we are testing.
|
||||
* @param entityMetadata The metadata for the entity within this load attributes context.
|
||||
* @return Whether the attribute should be loaded.
|
||||
*/
|
||||
public boolean isIncluded(AttributeMetadata attributeMetadata, EntityMetadata entityMetadata) {
|
||||
boolean result = (!attributeMetadata.isBackReference()) && entityMetadata != null ? entityMetadata.getAttributes().containsValue(attributeMetadata.getNumber()) : true;
|
||||
|
||||
if(result) {
|
||||
result = (!((attributeMetadata.isCalculated() && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_CALCULATED_ATTRIBUTES) != 0) ||
|
||||
(attributeMetadata.isCollection() && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_COLLECTION_ATTRIBUTES) != 0) ||
|
||||
(attributeMetadata.isPartOf() && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_PART_OF_ATTRIBUTES) != 0) ||
|
||||
(attributeMetadata.isCollection() || attributeMetadata.isWeak() || attributeMetadata.isCollection() || attributeMetadata.isCalculated()) && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_STANDARD_ATTRIBUTES) != 0) ||
|
||||
(attributeMetadata.isWeak() && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_WEAK_ATTRIBUTES) != 0) ||
|
||||
(attributeMetadata.isAttributeBound() && (getOptions() & LOAD_ATTRIBUTE_OPTION_EXCLUDE_BOUND_ATTRIBUTES) != 0));
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//isIncluded()//
|
||||
/**
|
||||
* Loads the attributes for the instance and all navigable objects in the logical object.
|
||||
* Note: Only objects that belong to the same logical object as the passed instance will be navigated. It is possible that the lock will be temporarily lost while lazy loading non-part-of references in the logical object.
|
||||
* @param The initial object whose attributes are to be loaded.
|
||||
*/
|
||||
private void loadAttributes(Object instance) {
|
||||
Object next = instance;
|
||||
|
||||
while(next != null) {
|
||||
if(!processedObjects.containsValue(next)) {
|
||||
TypeCallbackHandler callback = MetadataService.getSingleton().getTypeMetadata(next.getClass()).getTypeCallbackHandler();
|
||||
|
||||
if(callback != null) {
|
||||
callback.loadAttributes(next, this);
|
||||
}//if//
|
||||
|
||||
processedObjects.add(next);
|
||||
}//if//
|
||||
|
||||
next = pendingObjects.getSize() > 0 ? pendingObjects.remove(pendingObjects.getSize() - 1) : null;
|
||||
}//while//
|
||||
}//loadAttributes()//
|
||||
/**
|
||||
* Includes the instance in the set of objects whose attributes must be loaded within this context.
|
||||
* @param instance The object to whose attributes require loading.
|
||||
*/
|
||||
public void include(Object instance) {
|
||||
//If the new value is not in the processed objects set and it supports containment (we never navigate to non-part-of objects) then add it to the pending objects.//
|
||||
if((instance instanceof ISupportsContainment) && (!processedObjects.containsValue(instance))) {
|
||||
pendingObjects.add(instance);
|
||||
}//if//
|
||||
}//include()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.attribute.AttributeSupport.IAttributeChangeListener#attributeChanged(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public void attributeChanged(Object oldValue, Object newValue) {
|
||||
include(newValue);
|
||||
}//attributeChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#startChanges(int)
|
||||
*/
|
||||
public void startChanges(int changeCount) {
|
||||
//Ignored.//
|
||||
}//startChanges()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#allRemoved()
|
||||
*/
|
||||
public void removingAll() {
|
||||
//Ignored.//
|
||||
}//removingAll()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#valueAdded(java.lang.Object)
|
||||
*/
|
||||
public void valueAdded(Object value) {
|
||||
include(value);
|
||||
}//valueAdded()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#valueRemoved(java.lang.Object)
|
||||
*/
|
||||
public void valueRemoved(Object value) {
|
||||
//Ignored.//
|
||||
}//valueRemoved()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.util.IInlineCollectionObserver#stopChanges()
|
||||
*/
|
||||
public void stopChanges() {
|
||||
//Ignored.//
|
||||
}//stopChanges()//
|
||||
/**
|
||||
* Releases the context and cleans up any left over resources.
|
||||
* <p>Warning: The caller must hold the monitor lock for the initial instance passed to the context during construction.</p>
|
||||
*/
|
||||
public void release() {
|
||||
IIterator iterator = processedObjects.iterator();
|
||||
|
||||
//Notify the processed objects to allow them to cleanup.//
|
||||
while(iterator.hasNext()) {
|
||||
Object instance = iterator.next();
|
||||
TypeCallbackHandler callback = MetadataService.getSingleton().getTypeMetadata(instance.getClass()).getTypeCallbackHandler();
|
||||
|
||||
if(callback != null) {
|
||||
callback.postLoadAttributes(instance, this);
|
||||
}//if//
|
||||
}//while//
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the options specified for this context.
|
||||
* @return The optional options for the loading of the attributes. This should be ignored if a metadata container is specified.
|
||||
*/
|
||||
public int getOptions() {
|
||||
return options;
|
||||
}//getOptions()//
|
||||
/**
|
||||
* Gets the metadata specified for this context.
|
||||
* @return The metadata containing the type metadata indicating for each type which attributes to ensure are loaded and which attributes to navigate if part-of.
|
||||
*/
|
||||
public MetadataContainer getMetadataContainer() {
|
||||
return metadataContainer;
|
||||
}//getMetadataContainer()//
|
||||
/**
|
||||
* Gets the count of objects processed by this context.
|
||||
* @return The count of contained objects that were processed.
|
||||
*/
|
||||
public int getProcessedObjectCount() {
|
||||
return processedObjects.getSize();
|
||||
}//getProcessedObjectCount()//
|
||||
}//LoadAttributesContext//
|
||||
955
Foundation/src/com/foundation/metadata/MetadataService.java
Normal file
955
Foundation/src/com/foundation/metadata/MetadataService.java
Normal file
@@ -0,0 +1,955 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.metadata;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.common.io.*;
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
import com.foundation.common.MetadataContainer;
|
||||
import com.foundation.metadata.raw.*;
|
||||
import com.foundation.util.IKeyExtractor;
|
||||
|
||||
/**
|
||||
* This class manages the metadata for the software system.
|
||||
* <p><b>WARNING: All attributes must be declared in the defining class's static initializers for this system to work.
|
||||
* This is because the sub-classes will inherit these attributes and build on them.</b>
|
||||
*/
|
||||
public class MetadataService {
|
||||
/** Whether debugging should be turned on. */
|
||||
private static final boolean DEBUG = false;
|
||||
/** An object used to indicate there is no reflection metadata for a given registering class. */
|
||||
private static final Object NULL_METADATA = new Object();
|
||||
|
||||
/** The singleton reference for this MetadataService. */
|
||||
private static MetadataService singleton = new MetadataService();
|
||||
/** A thread local to hold a localized type map which prevents the need for synchronizing while accessing metadata. */
|
||||
private static ThreadLocal localTypeMapHolder = new ThreadLocal();
|
||||
|
||||
/** The map of TypeMetadata objects by the type's class object. */
|
||||
private LiteHashMap typeMap = new LiteHashMap(40);
|
||||
/** The map of InterfaceMetadata objects by the interface's class object. */
|
||||
private LiteHashMap interfaceMap = new LiteHashMap(40);
|
||||
/** A map of RepositoryMetadata objects by the repositories' ID. */
|
||||
private LiteHashMap repositoryMap = new LiteHashMap(5);
|
||||
/** A collection of all RawRepository metadata objects loaded by this MetadataService (but not necessarily in use). */
|
||||
private IList rawMetadata = new LiteList(10, 20);
|
||||
/** A mapping of RepositoryMetadata objects to their RawRepository metadata objects. */
|
||||
private LiteHashMap repositoryMetadataToRawRepositoryMap = new LiteHashMap(5);
|
||||
/** The next available global event number. This should be incremented by one each time it is used. */
|
||||
private int nextGlobalEventNumber = EventMetadata.GLOBAL_EVENT_NUMBER_OFFSET;
|
||||
/** The mapping of view metadata indexed by the defining view class. */
|
||||
private LiteHashMap viewMetadataMap = new LiteHashMap(40);
|
||||
/**
|
||||
* Gets the one and only metadata service object for this process.
|
||||
* @return The only metadata service object in this process (unless multiple class loaders are used).
|
||||
*/
|
||||
public static MetadataService getSingleton() {
|
||||
return singleton;
|
||||
}//getSingleton()//
|
||||
/**
|
||||
* MetadataService constructor.
|
||||
*/
|
||||
private MetadataService() {
|
||||
super();
|
||||
}//MetadataService()//
|
||||
/**
|
||||
* Gets the metadata associated with a view's class.
|
||||
* @param registeringClass The class that registered the metadata.
|
||||
* @return The metadata registered by the class, or null if the view doesn't define any metadata.
|
||||
*/
|
||||
public synchronized MetadataContainer getReflectionMetadata(Class registeringClass) {
|
||||
Object result = NULL_METADATA;
|
||||
|
||||
if(registeringClass != null) {
|
||||
result = viewMetadataMap.get(registeringClass);
|
||||
|
||||
if(result == null) {
|
||||
//Force the view class to initialize its metadata.//
|
||||
try {
|
||||
registeringClass.newInstance();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("ERROR: The class " + registeringClass.getName() + " must define a public default constructor otherwise the reflection metadata cannot be loaded.", e);
|
||||
}//catch//
|
||||
|
||||
result = viewMetadataMap.get(registeringClass);
|
||||
|
||||
if(result == null) {
|
||||
viewMetadataMap.put(registeringClass, result = NULL_METADATA);
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result == NULL_METADATA ? null : (MetadataContainer) result;
|
||||
}//getReflectionMetadata()//
|
||||
/**
|
||||
* Used by classes to register metadata used by the reflection system.
|
||||
* @param registeringClass The class that is registering metadata.
|
||||
* @param metadata The metadata for the class.
|
||||
*/
|
||||
public synchronized void setReflectionMetadata(Class registeringClass, MetadataContainer metadata) {
|
||||
viewMetadataMap.put(registeringClass, metadata);
|
||||
}//setReflectionMetadata()//
|
||||
/**
|
||||
* Adds the type metadata from the class to the TypeMetadata object.
|
||||
* @param type The class the metadata relates to.
|
||||
* @param managementType The management type identifier. Must be one of the TypeMetadata.MANAGEMENT_TYPE_xxx identifiers.
|
||||
*/
|
||||
public void addTypeMetadata(Class type, int managementType) {
|
||||
internalAddTypeMetadata(type, managementType);
|
||||
}//addTypeMetadata()//
|
||||
/**
|
||||
* Adds an event to the type's metadata. This is called by an entity as its class is being loaded. It should not ever be called at any other time.
|
||||
* <p>Note: Registering an attribute automatically registers an event which uses the same attribute identifier.</p>
|
||||
* <p>Note: Events use a different numbering system (beginning at 0x0FFFFFFF) so that they can peacefully co-exist with the attribute identifiers which double as events.</p>
|
||||
* @param type The class which has declared the event.
|
||||
* @param name The name of the event. This should be unique for the class and all super classes.
|
||||
* @return The event's unique (relative to the class hierarchy it was declared in) number which can be used to easily reference the event.
|
||||
*/
|
||||
public Event addEvent(Class type, String name) {
|
||||
return internalAddEvent(type, name);
|
||||
}//addEvent()//
|
||||
/**
|
||||
* Adds an attribute to the type's metadata. This is called by the AttributeSupport class when a class registers an attribute. It should not ever be called at any other time.
|
||||
* @param type The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param options The attribute's options.
|
||||
* @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 attribute's unique (relative to the class hierarchy it was declared in) number which can be used to easily reference the attribute.
|
||||
*/
|
||||
public Attribute addAttribute(Class type, String name, int options, Class valueType) {
|
||||
return internalAddAttribute(type, name, options, valueType, null, -1, -1, null, false);
|
||||
}//addAttribute()//
|
||||
/**
|
||||
* Adds an attribute to the type's metadata. This is called by the AttributeSupport class when a class registers an attribute. It should not ever be called at any other time.
|
||||
* @param type The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param options The attribute's options.
|
||||
* @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 attribute's unique (relative to the class hierarchy it was declared in) number which can be used to easily reference the attribute.
|
||||
*/
|
||||
public Attribute addAttribute(Class type, String name, int options, Class valueType, Object defaultValue) {
|
||||
return internalAddAttribute(type, name, options, valueType, null, -1, -1, defaultValue, true);
|
||||
}//addAttribute()//
|
||||
/**
|
||||
* Adds an attribute to the type's metadata. This is called by the AttributeSupport class when a class registers an attribute. It should not ever be called at any other time.
|
||||
* @param type The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param boundType The class of referencing object (part-of only) that will set the attribute's value.
|
||||
* @return The attribute's unique (relative to the class hierarchy it was declared in) number which can be used to easily reference the attribute.
|
||||
*/
|
||||
public Attribute addAttribute(Class type, String name, Class boundType) {
|
||||
return internalAddAttribute(type, name, 0, null, boundType, -1, -1, null, false);
|
||||
}//addAttribute()//
|
||||
/**
|
||||
* Adds an attribute to the type's metadata. This is called by the AttributeSupport class when a class registers an attribute. It should not ever be called at any other time.
|
||||
* @param type The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param relatedAttribute The attribute whose value defines the boundAttribute and whose boundAttribute value is used to populate this attribute's value.
|
||||
* @param boundAttribute The attribute on the related attribute's value to which this attribute value is bound (or identitical).
|
||||
* @return The attribute's unique (relative to the class hierarchy it was declared in) number which can be used to easily reference the attribute.
|
||||
*/
|
||||
public Attribute addAttribute(Class type, String name, int relatedAttribute, int boundAttribute) {
|
||||
return internalAddAttribute(type, name, 0, null, null, relatedAttribute, boundAttribute, null, false);
|
||||
}//addAttribute()//
|
||||
/**
|
||||
* Adds repository metadata to this metadata service. The actual metadata is stored in the application's metadata database which must have already been loaded by calling loadMetadata(Application).
|
||||
* @param identifier The repositories' identifier as used to reference the repository when specifying attribute metadata or when generating transactions.
|
||||
* @param resourceManagerClassName The name of the resource manager class (including the package name).
|
||||
* @param properties The map of properties that the resource manager will use to communicate with the resource. The properties should be defined by the specific resource manager.
|
||||
* @see #loadMetadata(Application)
|
||||
*/
|
||||
public synchronized boolean addRepository(Object identifier) {
|
||||
boolean found = false;
|
||||
|
||||
for(int rawMetadataIndex = 0; (!found) && (rawMetadataIndex < getRawMetadata().getSize()); rawMetadataIndex++) {
|
||||
RawRepository rawRepository = (RawRepository) getRawMetadata().get(rawMetadataIndex);
|
||||
|
||||
//Determine if we have found the correct raw metadata for the repository.//
|
||||
if(rawRepository.getRepositoryId().equals(identifier)) {
|
||||
RepositoryMetadata repositoryMetadata = null;
|
||||
LiteHashMap properties = rawRepository.getProperties();
|
||||
|
||||
try {
|
||||
repositoryMetadata = new RepositoryMetadata(rawRepository.getApplication(), identifier, rawRepository.getRepositoryClass(), properties);
|
||||
}//try//
|
||||
catch(ClassNotFoundException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
//Save the mapping between the repositories' unique ID and the RepositoryMetadata.//
|
||||
repositoryMap.put(identifier, repositoryMetadata);
|
||||
//Save the mapping between the RepositoryMetadata and the RawRepository.//
|
||||
repositoryMetadataToRawRepositoryMap.put(repositoryMetadata, rawRepository);
|
||||
//Set the found flag indicating the repository has been correctly initialized.//
|
||||
found = true;
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
if(!found) {
|
||||
throw new RuntimeException("ERROR: Unable to locate the raw metadata for the repository. Please make sure there is metadata for this application and the ID for the repository is correct.");
|
||||
}//if//
|
||||
|
||||
return found;
|
||||
}//addRepository()//
|
||||
/**
|
||||
* 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 synchronized IList getAttributeNames(Class type) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
return typeMetadata != null ? typeMetadata.getAttributeNames() : LiteList.EMPTY_LIST;
|
||||
}//getAttributeNames()//
|
||||
/**
|
||||
* Gets the number assigned the attribute with the given name.
|
||||
* @param type The class that declares (or extends the class that declares) the attribute.
|
||||
* @param attributeName The unique (within the type hierarchy) name of the attribute.
|
||||
* @return The unique number which is used to reference the attribute.
|
||||
*/
|
||||
public int getAttributeNumber(Class type, String attributeName) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
if(typeMetadata == null) {
|
||||
throw new IllegalArgumentException("Invalid class: " + type.getName() + ". The class must use enhanced attributes or methods.");
|
||||
}//if//
|
||||
|
||||
return typeMetadata.getAttributeNumber(attributeName);
|
||||
}//getAttributeNumber()//
|
||||
/**
|
||||
* Gets the number assigned the event with the given name.
|
||||
* @param type The class that declares (or extends the class that declares) the event.
|
||||
* @param eventName The unique (within the type hierarchy) name of the event.
|
||||
* @return The unique number which is used to reference the event.
|
||||
*/
|
||||
public int getEventNumber(Class type, String eventName) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
if(typeMetadata == null) {
|
||||
throw new IllegalArgumentException("Invalid class: " + type.getName() + ". The class must use enhanced attributes or methods.");
|
||||
}//if//
|
||||
|
||||
//TODO: It would be nice not to synchronize here, but it is concievable that a user would request the event number/name prior to instantiating the class.//
|
||||
synchronized(this) {
|
||||
if(!typeMetadata.isFinished()) {
|
||||
typeMetadata.finish();
|
||||
}//if//
|
||||
}//synchronized//
|
||||
|
||||
return typeMetadata.getEventNumber(eventName);
|
||||
}//getEventNumber()//
|
||||
/**
|
||||
* Gets the sequential and unique number for the event given the name assigned to the event when the event or attribute identifier was defined.
|
||||
* @param type The class that the sequence will be for. Note: The user should not pass in the defining class if the sequence is for a subclass.
|
||||
* @param eventName The unique name assigned to the event.
|
||||
* @return The unique and sequential number for the event starting with zero. This number allows users to easily store event information in an array instead of a less efficient hash map.
|
||||
* @throws java.lang.IllegalArgumentException If the event name was not associated with one of the events or attribute defined for the type.
|
||||
*/
|
||||
public int getSequentialEventNumber(Class type, String eventName) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
if(typeMetadata == null) {
|
||||
throw new IllegalArgumentException("Invalid class: " + type.getName() + ". The class must use enhanced attributes or methods.");
|
||||
}//if//
|
||||
|
||||
return typeMetadata.getSequentialEventNumber(typeMetadata.getEventNumber(eventName));
|
||||
}//getSequentialEventNumber()//
|
||||
/**
|
||||
* Gets the sequential and unique number for the event given the number assigned to the event identifier or as returned by the getEventNumber(Class, String) method.
|
||||
* @param type The class that the sequence will be for. Note: The user should not pass in the defining class if the sequence is for a subclass.
|
||||
* @param eventNumber The unique number assigned to the event identifier.
|
||||
* @return The unique and sequential number for the event starting with zero. This number allows users to easily store event information in an array instead of a less efficient hash map.
|
||||
* @throws java.lang.IllegalArgumentException If the event name was not associated with one of the events or attribute defined for the type.
|
||||
*/
|
||||
public int getSequentialEventNumber(Class type, int eventNumber) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
if(typeMetadata == null) {
|
||||
throw new IllegalArgumentException("Invalid class: " + type.getName() + ". The class must use enhanced attributes or methods.");
|
||||
}//if//
|
||||
|
||||
return typeMetadata.getSequentialEventNumber(eventNumber);
|
||||
}//getSequentialEventNumber()//
|
||||
/**
|
||||
* Gets the name assigned the event with the given number.
|
||||
* @param type The class that declares (or extends the class that declares) the event.
|
||||
* @param eventNumber The unique (within the type hierarchy) number of the event.
|
||||
* @return The unique name which is used to reference the event.
|
||||
*/
|
||||
public String getEventName(Class type, int eventNumber) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
|
||||
if(typeMetadata == null) {
|
||||
throw new IllegalArgumentException("Invalid class: " + type.getName() + ". The class must use enhanced attributes or methods.");
|
||||
}//if//
|
||||
|
||||
//TODO: It would be nice not to synchronize here, but it is concievable that a user would request the event number/name prior to instantiating the class.//
|
||||
synchronized(this) {
|
||||
if(!typeMetadata.isFinished()) {
|
||||
typeMetadata.finish();
|
||||
}//if//
|
||||
}//synchronized//
|
||||
|
||||
return typeMetadata.getEventName(eventNumber);
|
||||
}//getEventName()//
|
||||
/**
|
||||
* Gets the last used attribute number for the specified type hierarchy.
|
||||
* @param type The type whose last used attribute number should be found.
|
||||
* @return The last used attribute number for the requested type.
|
||||
*/
|
||||
public synchronized int getLastAttributeNumber(Class type) {
|
||||
return getTypeMetadata(type).getLastAttributeNumber();
|
||||
}//getLastAttributeNumber()//
|
||||
/**
|
||||
* Gets the raw metadata collection for the application(s).
|
||||
* <p>WARNING: This method should only be used synchronously and is intended for use only by metadata builder tools.
|
||||
* @return A list of com.foundation.metadata.raw.Repository objects for the loaded application(s).
|
||||
*/
|
||||
public synchronized IList getRawMetadata() {
|
||||
return rawMetadata;
|
||||
}//getRawMetadata()//
|
||||
/**
|
||||
* Gets the repository metadata for the given repository identifier.
|
||||
* @param identifier The repositories' identifier as used to reference the repository when specifying attribute metadata or when generating transactions.
|
||||
* @return The repository metadata containing information about the repository.
|
||||
*/
|
||||
public synchronized RepositoryMetadata getRepositoryMetadata(Object identifier) {
|
||||
RepositoryMetadata metadata = (RepositoryMetadata) repositoryMap.get(identifier);
|
||||
|
||||
if((metadata == null) && (addRepository(identifier))) {
|
||||
metadata = (RepositoryMetadata) repositoryMap.get(identifier);
|
||||
}//if//
|
||||
|
||||
return metadata;
|
||||
}//getRepositoryMetadata()//
|
||||
/**
|
||||
* Gets the fully initialized type metadata object for the requested class.
|
||||
* @param type The class whose type metadata should be returned.
|
||||
* @return The type metadata object for the expected class. This will be <code>null</code> if the metadata does not exist.
|
||||
*/
|
||||
public TypeMetadata getTypeMetadata(Class type) {
|
||||
LiteHashMap localTypeMetadataMap = (LiteHashMap) localTypeMapHolder.get();
|
||||
TypeMetadata result = null;
|
||||
|
||||
//Lazily initialize the local cache.//
|
||||
if(localTypeMetadataMap == null) {
|
||||
localTypeMetadataMap = new LiteHashMap(20);
|
||||
localTypeMapHolder.set(localTypeMetadataMap);
|
||||
}//if//
|
||||
|
||||
//Look for the metadata in the local cache.//
|
||||
result = (TypeMetadata) localTypeMetadataMap.get(type);
|
||||
|
||||
if(result == null) {
|
||||
//Get the type metadata from the shared cache.//
|
||||
result = internalGetTypeMetadata(type);
|
||||
|
||||
if(result != null) {
|
||||
//Add the metadata to the local cache.//
|
||||
localTypeMetadataMap.put(type, result);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getTypeMetadata()//
|
||||
/**
|
||||
* Gets the repository metadata for the given type.
|
||||
* <p>NOTE: This method will try to create the metadata if necessary by creating a new instance of the type (to force the dynamic metadata to load).
|
||||
* @param type The class to get metadata for.
|
||||
* @param repositoryIdentifier The repository to get metadata for.
|
||||
* @return The repository metadata for the specified class. This will be <code>null</code> if the metadata does not exist.
|
||||
*/
|
||||
public synchronized TypeRepositoryMetadata getTypeRepositoryMetadata(Class type, Object repositoryIdentifier) {
|
||||
TypeMetadata typeMetadata = getTypeMetadata(type);
|
||||
TypeRepositoryMetadata typeRepositoryMetadata = null;
|
||||
|
||||
if(typeMetadata != null) {
|
||||
typeRepositoryMetadata = typeMetadata.getTypeRepositoryMetadata(repositoryIdentifier);
|
||||
|
||||
if(typeRepositoryMetadata == null) {
|
||||
RepositoryMetadata repository = getRepositoryMetadata(repositoryIdentifier);
|
||||
|
||||
if(repository != null) {
|
||||
if(setupTypeRepositoryMetadata(typeMetadata, repository)) {
|
||||
typeRepositoryMetadata = typeMetadata.getTypeRepositoryMetadata(repositoryIdentifier);
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return typeRepositoryMetadata;
|
||||
}//getTypeMetadata()//
|
||||
/**
|
||||
* Adds the type metadata from the class to the TypeMetadata object.
|
||||
* @param type The class the metadata relates to.
|
||||
* @param managementType The management type identifier. Must be one of the TypeMetadata.MANAGEMENT_TYPE_xxx identifiers.
|
||||
*/
|
||||
private synchronized void internalAddTypeMetadata(Class type, int managementType) {
|
||||
TypeMetadata typeMetadata = internalGetOrCreateTypeMetadata(type);
|
||||
|
||||
typeMetadata.setManagementType(managementType);
|
||||
}//internalAddTypeMetadata()//
|
||||
/**
|
||||
* Adds an event to the type's metadata.
|
||||
* @param type The class which has declared this event.
|
||||
* @param name The name of the event.
|
||||
* @return The event's unique (relative to the class hierarchy it was declared in) identifier which can be used to easily reference the event.
|
||||
*/
|
||||
private synchronized Event internalAddEvent(Class type, String name) {
|
||||
return type.isInterface() ? internalAddInterfaceEvent(type, name) : internalAddClassEvent(type, name);
|
||||
}//internalAddEvent()//
|
||||
/**
|
||||
* Adds an event to the interface's metadata.
|
||||
* @param type The interface which has declared this event.
|
||||
* @param name The name of the event.
|
||||
* @return The event's unique global identifier which can be used to easily reference the event for any implementor of the interface.
|
||||
*/
|
||||
private Event internalAddInterfaceEvent(Class type, String name) {
|
||||
EventMetadata eventMetadata = new EventMetadata(type, name);
|
||||
InterfaceMetadata interfaceMetadata = internalGetOrCreateInterfaceMetadata(type);
|
||||
Event result = null;
|
||||
|
||||
if(interfaceMetadata.isFinished()) {
|
||||
throw new IllegalArgumentException("An attempt to register an event named '" + name + "' with an incorrect class reference has occured. Please check the registering class to ensure its events pass the correct class reference when registering.");
|
||||
}//if//
|
||||
|
||||
//Set the global event number for this event.//
|
||||
eventMetadata.setNumber(nextGlobalEventNumber++);
|
||||
eventMetadata.setIdentifier(result = new Event(eventMetadata));
|
||||
//Associate the event metadata with the type metadata.//
|
||||
interfaceMetadata.addEventMetadata(eventMetadata);
|
||||
|
||||
return result;
|
||||
}//internalAddInterfaceEvent()//
|
||||
/**
|
||||
* Adds an event to the class' metadata.
|
||||
* @param type The class which has declared this event.
|
||||
* @param name The name of the event.
|
||||
* @return The event's unique (relative to the class hierarchy it was declared in) identifier which can be used to easily reference the event.
|
||||
*/
|
||||
private Event internalAddClassEvent(Class type, String name) {
|
||||
EventMetadata eventMetadata = new EventMetadata(type, name);
|
||||
TypeMetadata typeMetadata = internalGetOrCreateTypeMetadata(type);
|
||||
Event result = null;
|
||||
|
||||
if(typeMetadata.isFinished()) {
|
||||
throw new IllegalArgumentException("An attempt to register an event named '" + name + "' with an incorrect class reference has occured. Please check the registering class to ensure its events pass the correct class reference when registering.");
|
||||
}//if//
|
||||
|
||||
//Associate the event metadata with the type metadata.//
|
||||
typeMetadata.addEventMetadata(eventMetadata);
|
||||
eventMetadata.setNumber(eventMetadata.getHierarchyNumber() + EventMetadata.CUSTOM_EVENT_NUMBER_OFFSET); //Offset the number for custom events to avoid clashing with attribute numbers which are also events.//
|
||||
eventMetadata.setIdentifier(result = new Event(eventMetadata));
|
||||
|
||||
return result;
|
||||
}//internalAddClassEvent()//
|
||||
/**
|
||||
* Adds an attribute to the type's metadata.
|
||||
* @param type The class which has declared this attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param options The attribute options or zero.
|
||||
* @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 boundType The class of object referencing the declaring type's instance as part-of. If another object references an instance of the declaring type as part-of, and if the referencing object's type is or extends the given class, then the attribute's value will be set to the referencing object. This is null if the attribute's value is not dependant on a referencing object.
|
||||
* @param relatedAttribute Identifies the attribute in the declaring type whose value is used to access the bound attribute's value. This is -1 if the attribute is not bound to another object's attribute's value.
|
||||
* @param boundAttributeNumber The attribute on the related attribute's value to which this attribute value is bound (or identitical), or -1 if the referencing object its self should be the value.
|
||||
* @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).
|
||||
* @param useDefaultValue Whether the default value is valid and should be used.
|
||||
* @return The attribute's unique (relative to the class hierarchy it was declared in) identifier which can be used to easily reference the attribute.
|
||||
*/
|
||||
private synchronized Attribute internalAddAttribute(Class type, String name, int options, Class valueType, Class boundType, int relatedAttribute, int boundAttribute, Object defaultValue, boolean useDefaultValue) {
|
||||
AttributeMetadata attributeMetadata;
|
||||
TypeMetadata typeMetadata = internalGetOrCreateTypeMetadata(type);
|
||||
IIterator repositoryIdentifierIterator = repositoryMap.keyIterator();
|
||||
Attribute result = null;
|
||||
|
||||
if(boundType != null) {
|
||||
attributeMetadata = new AttributeMetadata(type, name, boundType);
|
||||
}//if//
|
||||
else if((relatedAttribute != -1) && (boundAttribute != -1)) {
|
||||
attributeMetadata = new AttributeMetadata(type, name, relatedAttribute, boundAttribute);
|
||||
}//else if//
|
||||
else {
|
||||
attributeMetadata = new AttributeMetadata(type, name, options, valueType);
|
||||
}//else//
|
||||
|
||||
if(typeMetadata.isFinished()) {
|
||||
throw new IllegalArgumentException("An attempt to register an attribute named '" + name + "' with the incorrect class reference has occured. Please check the registering class(s) to ensure its attributes pass the correct class reference when registering. The other possible cause is that the registering class is instantiating prior to completing all attribute registrations (example: Creating a singleton or running a static block of code.). Perform the instantiation after all variable declarations.");
|
||||
}//if//
|
||||
|
||||
//Associate the attribute metadata with the type metadata.//
|
||||
typeMetadata.addAttributeMetadata(attributeMetadata);
|
||||
result = new Attribute(attributeMetadata);
|
||||
attributeMetadata.setIdentifier(result);
|
||||
|
||||
//Setup the repository metadata associations.//
|
||||
while(repositoryIdentifierIterator.hasNext()) {
|
||||
Object repositoryIdentifier = repositoryIdentifierIterator.next();
|
||||
TypeRepositoryMetadata typeRepositoryMetadata = typeMetadata.getTypeRepositoryMetadata(repositoryIdentifier);
|
||||
|
||||
if(typeRepositoryMetadata != null) {
|
||||
AttributeRepositoryMetadata attributeRepositoryMetadata = typeRepositoryMetadata.getAttributeRepositoryMetadata(attributeMetadata.getName());
|
||||
|
||||
if(attributeRepositoryMetadata != null) {
|
||||
//Re-save the attribute repository metadata in the type repository metadata.//
|
||||
typeRepositoryMetadata.addAttributeRepositoryMetadata(attributeMetadata.getName(), attributeMetadata.getNumber(), attributeRepositoryMetadata);
|
||||
//Save the reference to the attribute metadata in the attribute repository metadata.//
|
||||
attributeRepositoryMetadata.setAttributeMetadata(attributeMetadata);
|
||||
//Add the attribute repository metadata to the attribute metadata.//
|
||||
attributeMetadata.addAttributeRepositoryMetadata(repositoryIdentifier, attributeRepositoryMetadata);
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
if(useDefaultValue) {
|
||||
attributeMetadata.setDefaultValue(defaultValue);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//internalAddAttribute()//
|
||||
/**
|
||||
* Gets or creates the type metadata object for the requested class.
|
||||
* <p>Note: This method will create a new blank type metadata if necessary. This is only for internal use.
|
||||
* @param type The class whose type metadata should be returned.
|
||||
* @return The type metadata object for the expected class.
|
||||
*/
|
||||
private TypeMetadata internalGetOrCreateTypeMetadata(Class type) {
|
||||
TypeMetadata typeMetadata = (TypeMetadata) typeMap.get(type);
|
||||
|
||||
//Check to see if we already have this type metadata object.//
|
||||
if(typeMetadata == null) {
|
||||
if(type.isInterface()) {
|
||||
Debug.log(" ** ERROR: Unable to get type metadata for an interface: " + type);
|
||||
}//if//
|
||||
else {
|
||||
//Determine if there should be a parent type metadata object.//
|
||||
if(!type.getSuperclass().equals(Object.class)) {
|
||||
ISet interfaces = new LiteHashSet(20);
|
||||
InterfaceMetadata[] interfaceMetadata = null;
|
||||
|
||||
//Collect the interface metadata directly or indirectly implemented by the class and convert the metadata into an array.//
|
||||
collectInterfaceMetadata(type, interfaces, new LiteHashSet(60), true);
|
||||
interfaceMetadata = new InterfaceMetadata[interfaces.getSize()];
|
||||
interfaces.toArray(interfaceMetadata);
|
||||
//Create the parent type metadata.//
|
||||
typeMetadata = new TypeMetadata(type, internalGetOrCreateTypeMetadata(type.getSuperclass()), interfaceMetadata);
|
||||
}//if//
|
||||
else {
|
||||
//Create a new type metadata without a parent.//
|
||||
typeMetadata = new TypeMetadata(type);
|
||||
}//else//
|
||||
|
||||
//Add the new type metadata to the map so it gets reused in the future.//
|
||||
typeMap.put(type, typeMetadata);
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
return typeMetadata;
|
||||
}//internalGetTypeMetadata()//
|
||||
/**
|
||||
* Gets or creates the type metadata object for the requested class.
|
||||
* <p>Note: This method will create a new blank interface metadata if necessary. This is only for internal use.</p>
|
||||
* @param interfaceType The interface class whose interface metadata should be returned.
|
||||
* @return The interface metadata object for the expected class.
|
||||
*/
|
||||
private InterfaceMetadata internalGetOrCreateInterfaceMetadata(Class interfaceType) {
|
||||
InterfaceMetadata interfaceMetadata = (InterfaceMetadata) interfaceMap.get(interfaceType);
|
||||
|
||||
//Check to see if we already have this type metadata object.//
|
||||
if(interfaceMetadata == null) {
|
||||
if(!interfaceType.isInterface()) {
|
||||
Debug.log(" ** ERROR: Unable to get interface metadata for a class: " + interfaceType);
|
||||
}//if//
|
||||
else {
|
||||
ISet interfaces = new LiteHashSet(20);
|
||||
InterfaceMetadata[] superInterfaceMetadata = null;
|
||||
|
||||
//Collect the interface metadata directly or indirectly implemented by the class and convert the metadata into an array.//
|
||||
collectInterfaceMetadata(interfaceType, interfaces, new LiteHashSet(60), true);
|
||||
superInterfaceMetadata = new InterfaceMetadata[interfaces.getSize()];
|
||||
interfaces.toArray(superInterfaceMetadata);
|
||||
//Create a new interface metadata.//
|
||||
interfaceMetadata = new InterfaceMetadata(interfaceType, superInterfaceMetadata);
|
||||
//Add the new type metadata to the map so it gets reused in the future.//
|
||||
interfaceMap.put(interfaceType, interfaceMetadata);
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
return interfaceMetadata;
|
||||
}//internalGetTypeMetadata()//
|
||||
/**
|
||||
* Gets the interface metadata for each implemented interface which has declared metadata.
|
||||
* @param type The class whose interface metadata should be collected.
|
||||
* @param results The collection of interface metadata for interfaces defining metadata only and which are implemented directly or indirectly by the given class.
|
||||
* @param searched A set of interfaces already searched so that we don't spend more time than necessary looking.
|
||||
* @param collect Whether the method call should collect results. This allows this recursive method to invalidate interfaces implemented by the other classes in the initial classes' hierarchy. The caller should always pass true.
|
||||
*/
|
||||
private void collectInterfaceMetadata(Class type, ISet results, ISet searched, boolean collect) {
|
||||
Class[] interfaces = type.getInterfaces();
|
||||
|
||||
//If the passed type is an interface then add it to the search path and if it has defined metadata then add it to the results.//
|
||||
if(type.isInterface()) {
|
||||
searched.add(type);
|
||||
|
||||
if(collect) {
|
||||
InterfaceMetadata interfaceMetadata = (InterfaceMetadata) interfaceMap.get(type);
|
||||
|
||||
if(interfaceMetadata != null) {
|
||||
results.add(interfaceMetadata);
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
else if(type.getSuperclass() != null) {
|
||||
//Collect metadata for the super class.//
|
||||
collectInterfaceMetadata(type.getSuperclass(), results, searched, true);
|
||||
}//if//
|
||||
|
||||
//Collect metadata for implemented or extended interfaces.//
|
||||
for(int interfaceIndex = 0; interfaceIndex < interfaces.length; interfaceIndex++) {
|
||||
if(!searched.containsValue(interfaces[interfaceIndex])) {
|
||||
collectInterfaceMetadata(interfaces[interfaceIndex], results, searched, collect);
|
||||
}//if//
|
||||
}//for//
|
||||
}//collectInterfaceMetadata()//
|
||||
/**
|
||||
* Gets the type metadata object for the requested class. All access to the type should go through the thread local under normal circumstances.
|
||||
* @param type The class whose type metadata should be returned.
|
||||
* @return The type metadata object for the expected class. This will be <code>null</code> if the metadata does not exist because it could not be created.
|
||||
*/
|
||||
private synchronized TypeMetadata internalGetTypeMetadata(Class type) {
|
||||
TypeMetadata result = (TypeMetadata) typeMap.get(type); //NOTE: Do not call internalGetTypeMetadata() because we don't want a new blank metadata object.//
|
||||
|
||||
if(result == null) {
|
||||
try {
|
||||
if((type.isInterface()) || (type.isPrimitive()) || (java.lang.reflect.Modifier.isAbstract(type.getModifiers()))) {
|
||||
Debug.log("ERROR: Unable to collect type metadata for a type that is primitive, abstract, or an interface.");
|
||||
//TODO: Throw exception?//
|
||||
}//if//
|
||||
else {
|
||||
//Force a blank metadata object to be created just in case the type does not define any metadata.//
|
||||
internalGetOrCreateTypeMetadata(type);
|
||||
|
||||
//Attempt to dyamically create a new type instance, thus forcing the metadata to be loaded into the blank type metadata object.//
|
||||
try {
|
||||
//Note: This is only necessary because some VM's don't run the static initializers until an instance of a class is created, or the static members are accessed.//
|
||||
//TODO: Is there a better way to force the static members to be initialized? Maybe interacting with the class object, or calling some static method, or accessing a public static member?//
|
||||
type.newInstance();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
//TODO: Can we force it to load by using the native instantiation method?//
|
||||
|
||||
//Note: I am not going to log this because it is possible that the type is an application or controller and has a good reason for having a non-public default constructor.//
|
||||
//Debug.log("ERROR: Unable to collect type metadata for a type that does not define a default constructor. Instantiation is necessary with some VM's to force the class to run its static initializers.");
|
||||
}//catch//
|
||||
|
||||
result = (TypeMetadata) typeMap.get(type);
|
||||
}//else//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
//If the type metadata has not been 'finished' then do so now so that the metadata consumers can easily access all the necessary data.//
|
||||
if((result != null) && (!result.isFinished())) {
|
||||
//Initialize the repository metadata for this type.//
|
||||
for(int index = 0; index < rawMetadata.getSize(); index++) {
|
||||
Object repositoryIdentifier = ((RawRepository) rawMetadata.get(index)).getRepositoryId();
|
||||
|
||||
setupTypeRepositoryMetadata(result, getRepositoryMetadata(repositoryIdentifier));
|
||||
}//while//
|
||||
|
||||
//Allow the type metadata to finish its initialization and optimize its self.//
|
||||
result.finish();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//internalGetTypeMetadata()//
|
||||
/**
|
||||
* Loads metadata for a perticular application.
|
||||
* <p>This method should be called by an application when setting up the metadata service for use by the application. It is also used by metadata generation tools.
|
||||
* @param application The application whose metadata should be loaded.
|
||||
*/
|
||||
public void loadMetadata(com.foundation.application.Application application) {
|
||||
Object metadataLocation = application.getMetadataLocation();
|
||||
|
||||
if(metadataLocation != null) {
|
||||
if(metadataLocation instanceof File) {
|
||||
if(DEBUG) {
|
||||
try {
|
||||
Debug.log("Loading metadata from: " + ((File) metadataLocation).getCanonicalPath());
|
||||
}//try//
|
||||
catch(IOException e) {
|
||||
Debug.log(e);
|
||||
}//catch()//
|
||||
}//if//
|
||||
|
||||
loadMetadata(application, (File) metadataLocation);
|
||||
}//if//
|
||||
else if(metadataLocation instanceof String) {
|
||||
File file = new File((String) metadataLocation);
|
||||
|
||||
if(DEBUG) {
|
||||
try {
|
||||
Debug.log("Loading metadata from: " + file.getCanonicalPath());
|
||||
}//try//
|
||||
catch(java.io.IOException e) {
|
||||
Debug.log(e);
|
||||
}//catch()//
|
||||
}//if//
|
||||
|
||||
loadMetadata(application, file);
|
||||
}//else if//
|
||||
else {
|
||||
Debug.log("Unable to load the application metadata since a location was not provided as either a java.lang.String, java.net.URL, or java.io.File.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
InputStream metadataContents = application.getMetadataContents();
|
||||
|
||||
if(metadataContents != null) {
|
||||
try {
|
||||
//TODO: Provide support for binary metadata.//
|
||||
loadMetadata(application, metadataContents, false);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
try {
|
||||
metadataContents.close();
|
||||
}//try//
|
||||
catch(IOException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
else if(DEBUG) {
|
||||
//This may not be an error if the application defines no linkages to repositories.//
|
||||
Debug.log("Unable to load application metadata since no location was provided.");
|
||||
}//else if//
|
||||
}//else//
|
||||
}//loadMetadata()//
|
||||
/**
|
||||
* Loads metadata for a perticular application.
|
||||
* <p>This method should be called by an application when setting up the metadata service for use by the application. It is also used by metadata generation tools.
|
||||
* @param metadataFile The file from which the metadata should be restored.
|
||||
*/
|
||||
public synchronized void loadMetadata(com.foundation.application.Application application, java.io.File metadataFile) {
|
||||
if((metadataFile != null) && (metadataFile.isFile()) && (metadataFile.canRead())) {
|
||||
java.io.InputStream in = null;
|
||||
boolean isBinary = !metadataFile.getName().endsWith(".xml");
|
||||
|
||||
try {
|
||||
in = new FileInputStream(new File(metadataFile.getCanonicalPath()));
|
||||
loadMetadata(application, in, isBinary);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Caught in MetadataService.loadMetadata(Application)", e);
|
||||
}//catch//
|
||||
finally {
|
||||
if(in != null) {
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
//Ignore.//
|
||||
}//catch//
|
||||
}//if//
|
||||
}//finally//
|
||||
}//if//
|
||||
}//loadMetadata()//
|
||||
/**
|
||||
* Loads metadata for a perticular application.
|
||||
* <p>This method should be called by an application when setting up the metadata service for use by the application. It is also used by metadata generation tools.
|
||||
* @param in The stream containing either the xml characters or the binary bits that will be expanded into raw metadata model objects.
|
||||
* @param isBinary Whether the stream is a binary stream versus an XML stream.
|
||||
*/
|
||||
public synchronized void loadMetadata(com.foundation.application.Application application, java.io.InputStream in, boolean isBinary) {
|
||||
IList metadata = null;
|
||||
|
||||
//Metadata can be either XML or binary.//
|
||||
if(!isBinary) {
|
||||
metadata = new XmlMetadata().loadMetadata(in);
|
||||
}//if//
|
||||
else {
|
||||
try {
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream(in.available());
|
||||
ObjectInputStream oin = new ObjectInputStream(bin, null, null);
|
||||
|
||||
bin.readFrom(in, in.available());
|
||||
metadata = (IList) oin.readObject();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Caught in MetadataService.loadMetadata(Application)", e);
|
||||
}//catch//
|
||||
}//else//
|
||||
|
||||
//Load the metadata into the collection of raw metadata objects.//
|
||||
if(metadata != null) {
|
||||
if(DEBUG) {
|
||||
IIterator iterator = metadata.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
RawRepository repository = (RawRepository) iterator.next();
|
||||
|
||||
Debug.log("Located metadata for: " + repository.getRepositoryId());
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
for(IIterator iterator = metadata.iterator(); iterator.hasNext(); ) {
|
||||
RawRepository repository = (RawRepository) iterator.next();
|
||||
|
||||
repository.setApplication(application);
|
||||
}//while//
|
||||
|
||||
rawMetadata.addAll(metadata);
|
||||
}//if//
|
||||
}//loadMetadata()//
|
||||
/**
|
||||
* Saves metadata for a perticular application.
|
||||
* <p>WARNING: This method should normally only be called durring design time by metadata generation tools.
|
||||
* @param application The application whose metadata should be saved.
|
||||
*/
|
||||
public void saveMetadata(com.foundation.application.Application application) {
|
||||
Object metadataLocation = application.getMetadataLocation();
|
||||
File fileLocation = metadataLocation instanceof File ? (File) metadataLocation : metadataLocation instanceof String ? new File((String) metadataLocation) : null;
|
||||
|
||||
if(fileLocation != null) {
|
||||
saveMetadata(fileLocation);
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("Unable to save the metadata since the location was not a string or file.");
|
||||
}//else//
|
||||
}//saveMetadata()//
|
||||
/**
|
||||
* Saves metadata for a perticular application.
|
||||
* <p>WARNING: This method should normally only be called durring design time by metadata generation tools.</p>
|
||||
* @param metadataFile The file where the metadata should be saved.
|
||||
*/
|
||||
public synchronized void saveMetadata(java.io.File metadataFile) {
|
||||
if((!metadataFile.exists()) || ((metadataFile.isFile()) && (metadataFile.canWrite()))) {
|
||||
try {
|
||||
FileOutputStream out = new FileOutputStream(metadataFile);
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream(5000, false);
|
||||
ObjectOutputStream oout = new ObjectOutputStream(bout, null);
|
||||
|
||||
oout.writeObject(rawMetadata);
|
||||
bout.writeTo(out);
|
||||
oout.close();
|
||||
out.close();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log("Caught in MetadataService.loadMetadata(Application)", e);
|
||||
}//catch//
|
||||
}//if//
|
||||
}//saveMetadata()//
|
||||
/**
|
||||
* Sets the raw metadata collection for the application(s).
|
||||
* <p>WARNING: This method should only be used synchronously and is intended for use only by metadata builder tools.</p>
|
||||
* @param rawMetadata A list of com.foundation.metadata.raw.Repository objects for the loaded application(s).
|
||||
*/
|
||||
public synchronized void setRawMetadata(IList rawMetadata) {
|
||||
this.rawMetadata = rawMetadata;
|
||||
}//setRawMetadata()//
|
||||
/**
|
||||
* Sets the callback handler used for the given class and all subclasses that don't otherwise specify a callback handler.
|
||||
* @param typeCallbackHandler The handler to be called for specific callbacks required by the attribute and metadata systems.
|
||||
*/
|
||||
public synchronized void setTypeCallbackHandler(Class type, TypeCallbackHandler typeCallbackHandler) {
|
||||
TypeMetadata typeMetadata = internalGetOrCreateTypeMetadata(type);
|
||||
|
||||
typeMetadata.setTypeCallbackHandler(typeCallbackHandler);
|
||||
}//setTypeCallbackHandler()//
|
||||
/**
|
||||
* Sets up the repository metadata for a perticular type and repository.
|
||||
* @param typeMetadata The TypeMetadata object for the type to setup repository metadata for.
|
||||
* @param repositoryMetadata The RepositoryMetadata object for the repository to setup the type metadata for.
|
||||
*/
|
||||
private boolean setupTypeRepositoryMetadata(TypeMetadata typeMetadata, RepositoryMetadata repositoryMetadata) {
|
||||
RawRepository rawRepository = (RawRepository) repositoryMetadataToRawRepositoryMap.get(repositoryMetadata);
|
||||
boolean found = false;
|
||||
|
||||
if((rawRepository != null) && (typeMetadata.getTypeRepositoryMetadata(repositoryMetadata.getRepositoryIdentifier()) == null)) {
|
||||
IIterator rawTypeIterator = rawRepository.getTypes().iterator();
|
||||
|
||||
while((!found) && (rawTypeIterator.hasNext())) {
|
||||
RawType rawType = (RawType) rawTypeIterator.next();
|
||||
|
||||
if(rawType.getTypeClassName().equals(typeMetadata.getTypeClass().getName())) {
|
||||
//Expand raw type metadata and all raw attribute metadata.//
|
||||
TypeRepositoryMetadata typeRepositoryMetadata = new TypeRepositoryMetadata(typeMetadata, repositoryMetadata.getRepositoryIdentifier());
|
||||
IIterator rawAttributeIterator = rawType.getAttributes().iterator();
|
||||
|
||||
typeRepositoryMetadata.setRepositoryTypeName(rawType.getRepositoryTypeName());
|
||||
|
||||
while(rawAttributeIterator.hasNext()) {
|
||||
RawAttribute rawAttribute = (RawAttribute) rawAttributeIterator.next();
|
||||
AttributeMetadata attributeMetadata = typeMetadata.getAttributeMetadata(rawAttribute.getAttributeName());
|
||||
boolean isVersion = rawAttribute.getMetadata() != null && rawAttribute.getMetadata().intValue() == RawAttribute.METADATA_VERSION;
|
||||
boolean isLogicalObjectId = rawAttribute.getMetadata() != null && rawAttribute.getMetadata().intValue() == RawAttribute.METADATA_LOGICAL_OBJECT_ID;
|
||||
AttributeRepositoryMetadata attributeRepositoryMetadata;
|
||||
LiteList joins = null;
|
||||
|
||||
//If there are joins then setup the collection of join metadata.//
|
||||
if(rawAttribute.getJoins().getSize() > 0) {
|
||||
IIterator joinIterator = rawAttribute.getJoins().iterator();
|
||||
|
||||
joins = new LiteList(rawAttribute.getJoins().getSize() + 1);
|
||||
|
||||
while(joinIterator.hasNext()) {
|
||||
RawAttributeJoin join = (RawAttributeJoin) joinIterator.next();
|
||||
String cls = join.getRepositoryClass();
|
||||
|
||||
if(cls == null) {
|
||||
cls = rawType.getRepositoryTypeName();
|
||||
}//if//
|
||||
|
||||
joins.add(new AttributeRepositoryMetadata.Join(join.getRepositoryName(), cls));
|
||||
}//while//
|
||||
|
||||
//Add a join for the attribute its self so that the joins collection contains all combinations of repository class (table in an RDB) and repository attribute (column in an RDB).//
|
||||
joins.add(new AttributeRepositoryMetadata.Join(rawAttribute.getExternalAttributeName(), rawAttribute.getRepositoryClass() == null ? rawType.getRepositoryTypeName() : rawAttribute.getRepositoryClass()));
|
||||
}//if//
|
||||
|
||||
attributeRepositoryMetadata = new AttributeRepositoryMetadata(typeRepositoryMetadata, attributeMetadata, rawAttribute.getExternalAttributeName(), rawAttribute.getRepositoryClass(), rawAttribute.getJavaType(), rawAttribute.getRepositoryType(), rawAttribute.getProperties(), rawAttribute.getIsKey().booleanValue(), rawAttribute.getIsAutoGenerated().booleanValue(), isLogicalObjectId, isVersion, joins, rawAttribute.getIsAutoUpdated().booleanValue(), rawAttribute.getAutoUpdateResultName(), rawAttribute.getSequenceName());
|
||||
|
||||
if(attributeMetadata != null) {
|
||||
typeRepositoryMetadata.addAttributeRepositoryMetadata(attributeMetadata.getName(), attributeMetadata.getNumber(), attributeRepositoryMetadata);
|
||||
attributeMetadata.addAttributeRepositoryMetadata(repositoryMetadata.getRepositoryIdentifier(), attributeRepositoryMetadata);
|
||||
attributeRepositoryMetadata.setAttributeMetadata(attributeMetadata);
|
||||
}//if//
|
||||
else {
|
||||
Debug.log("ERROR: There is an error in the metadata definition file. The metadata for " + rawRepository.getRepositoryId() + " : " + rawType.getTypeClassName() + " : " + rawAttribute.getAttributeName() + ". The problem is most likely caused by the metadata attribute not having a counterpart in the Java class.");
|
||||
Debug.halt();
|
||||
//Debug.log("This should not occur because the type metadata should be fully loaded by the time this thread gets here.");
|
||||
//typeRepositoryMetadata.addAttributeRepositoryMetadata(rawAttribute.getAttributeName(), attributeRepositoryMetadata);
|
||||
}//else//
|
||||
}//while//
|
||||
|
||||
//Save the type repository metadata reference in the type metadata.//
|
||||
typeMetadata.setTypeRepositoryMetadata(repositoryMetadata.getRepositoryIdentifier(), typeRepositoryMetadata);
|
||||
//Set the found flag indicating we found and setup the metadata.//
|
||||
found = true;
|
||||
}//if//
|
||||
}//while//
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("PROGRAM ERROR: Unable to locate the raw repository metadata for a perticular repository. This should not ever occur.");
|
||||
}//else//
|
||||
|
||||
return found;
|
||||
}//setupTypeRepositoryMetadata()//
|
||||
}//MetadataService//
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2002,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.metadata;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.foundation.application.IApplication;
|
||||
|
||||
/**
|
||||
* Encapsulates metadata about a single repository that the application might interact with.
|
||||
* This metadata will be used by the transaction service(s) to interact with the repository.
|
||||
*/
|
||||
public class RepositoryMetadata {
|
||||
private Object repositoryIdentifier = null; //The identifier for the repository. This identifier is used to reference the repository in other metadata, and when performing transactions.//
|
||||
private Class repositoryManagerClass = null; //The qualified name of the class to use as the resource manager for the repository.//
|
||||
private LiteHashMap repositoryProperties = new LiteHashMap(20); //Properties specific to this repository. These properties should be defined as identifiers in the resource manager class.//
|
||||
/** The application that provided the metadata for this repository. */
|
||||
private IApplication application;
|
||||
/**
|
||||
* RepositoryMetadata constructor.
|
||||
*/
|
||||
protected RepositoryMetadata() {
|
||||
super();
|
||||
}//RepositoryMetadata()//
|
||||
/**
|
||||
* RepositoryMetadata constructor.
|
||||
* @param application The application that created this metadata.
|
||||
* @param repositoryIdentifier The unique identifier for the repository. This is usually a string.
|
||||
* @param repositoryManagerClass The class object for the repository manager for this repository.
|
||||
* @param properties The optional property map used to setup the repository manager.
|
||||
*/
|
||||
public RepositoryMetadata(IApplication application, Object repositoryIdentifier, Class repositoryManagerClass, LiteHashMap properties) {
|
||||
super();
|
||||
|
||||
this.application = application;
|
||||
this.repositoryIdentifier = repositoryIdentifier;
|
||||
this.repositoryManagerClass = repositoryManagerClass;
|
||||
|
||||
if(properties != null) {
|
||||
this.repositoryProperties = properties;
|
||||
}//if//
|
||||
}//RepositoryMetadata()//
|
||||
/**
|
||||
* Gets the application that provided the metadata for this repository.
|
||||
* @return The application whose metadata generated this instance.
|
||||
*/
|
||||
public IApplication getApplication() {
|
||||
return application;
|
||||
}//getApplication()//
|
||||
/**
|
||||
* Gets the repositories' identifier.
|
||||
* This identifier is used to reference the repository in other metadata, and when performing transactions.
|
||||
* @return The identifier for the repository.
|
||||
*/
|
||||
public Object getRepositoryIdentifier() {
|
||||
return repositoryIdentifier;
|
||||
}//getRepositoryIdentifier()//
|
||||
/**
|
||||
* Gets the class for the repository manager for this repository.
|
||||
* @return The class for the repositories' manager.
|
||||
*/
|
||||
public Class getRepositoryManagerClass() {
|
||||
return repositoryManagerClass;
|
||||
}//getRepositoryManagerClass()//
|
||||
/**
|
||||
* Gets a property associated with this repository.
|
||||
* The properties allow the repository to be customized via the metadata in a generic manner.
|
||||
* The ResourceManager for the repository should define all necessary properties as public static identifiers.
|
||||
* @param propertyName The name of the property to get.
|
||||
* @return The value of the requested property. This value will be <code>null</code> if it has not been defined.
|
||||
*/
|
||||
public Object getRepositoryProperty(Object propertyName) {
|
||||
return repositoryProperties.get(propertyName);
|
||||
}//getRepositoryProperty()//
|
||||
/**
|
||||
* Sets a property associated with this repository.
|
||||
* The properties allow the repository to be customized via the metadata in a generic manner.
|
||||
* The ResourceManager for the repository should define all necessary properties as public static identifiers.
|
||||
* @param propertyName The name of the property to set.
|
||||
* @param propertyValue The value of the requested property. This value should be <code>null</code> if it is not defined.
|
||||
*/
|
||||
public void setRepositoryProperty(Object propertyName, Object propertyValue) {
|
||||
repositoryProperties.put(propertyName, propertyValue);
|
||||
}//setRepositoryProperty()//
|
||||
}//RepositoryMetadata//
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user