package org.freehep.util.parameterdatabase;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.freehep.xml.util.XMLWriter;
public class ParameterDatabase {
/**
* The hashtable which contains all of the parameters.
*/
protected Hashtable database;
/**
* This list contains all of the database listeners.
*/
protected LinkedList databaseListeners = new LinkedList();
/**
* Constructor creates the hashtable to hold all of the parameters. The
* default size is 10000 entries.
*/
public ParameterDatabase() {
this(10000);
}
/**
* Constructor creates the hashtable of the given initial size to hold all
* of the parameters.
*/
public ParameterDatabase(int size) {
database = new Hashtable(size);
}
/**
* This method will determine if the given parameter is local. That is, that
* the parameter is defined on the first object returned from the iterator.
*/
public boolean isParameterLocal(String parameterName, Iterator iterator) {
// Just check the first object to see if this is local.
if (iterator.hasNext()) {
// Create an appropriate ParameterKey.
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate value.
ParameterValue value = (ParameterValue) database.get(key);
return (value != null);
}
// The default return is false.
return false;
}
/**
* Retrieve a parameter from the database. The iterator must produce a
* sequence of Objects which correspond to the specific tables in which to
* try to find the parameter. The iteration will stop when the parameter is
* first found.
*/
public Object getParameter(String parameterName, Iterator iterator) {
// Loop until we find the parameter or run out of tables to check.
// This will return as soon as the parameter is found.
while (iterator.hasNext()) {
// Create an appropriate ParameterKey.
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate value.
ParameterValue value = (ParameterValue) database.get(key);
if (value != null)
return value.getValue();
}
// Return null because the given parameter wasn't found.
return null;
}
/**
* Set a parameter. Use the iterator to traverse a series of tables in which
* to find the given parameter. Only the first parameter with this name will
* be modified. This method returns true if the parameter was found and set;
* it returns false otherwise.
*/
public boolean setParameter(String parameterName, Object value,
Iterator iterator) {
// Parameter name and value cannot be null.
if (parameterName == null)
throw new IllegalArgumentException("parameterName cannot be null.");
if (value == null)
throw new IllegalArgumentException("value cannot be null.");
// Loop until we find the parameter or run out of tables to
// check. This will return as soon as the parameter is
// found.
while (iterator.hasNext()) {
// Create an appropriate ParameterKey.
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate ParameterValue and set it.
ParameterValue parameterValue = (ParameterValue) database.get(key);
if (parameterValue != null) {
parameterValue.setValue(value);
firePropertyChange(parameterName, parameterValue);
return true;
}
}
// Indicate that the parameter was not found.
return false;
}
/**
* Return a boolean indicating whether this parameter is defined using the
* given iterator to iterate through a set of objects.
*/
public boolean isParameterDefined(String parameterName, Iterator iterator) {
// Parameter name cannot be null.
if (parameterName == null)
throw new IllegalArgumentException("parameterName cannot be null.");
// Loop until we find the parameter or run out of tables to
// check. This will return as soon as the parameter is
// found.
while (iterator.hasNext()) {
Object object = iterator.next();
if (isParameterDefined(parameterName, object))
return true;
}
// Indicate that the parameter was not found.
return false;
}
/**
* Return a boolean indicating whether this parameter is defined for the
* given object.
*/
public boolean isParameterDefined(String parameterName, Object object) {
// Parameter name and object cannot be null.
if (parameterName == null)
throw new IllegalArgumentException("parameterName cannot be null.");
if (object == null)
throw new IllegalArgumentException("object cannot be null.");
// Create an appropriate ParameterKey.
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate ParameterValue and set it.
ParameterValue parameterValue = (ParameterValue) database.get(key);
return (parameterValue != null);
}
/**
* Add a parameter to the database. This will only add the parameter to the
* first object returned by the given iterator. After the parameter is
* added, the listener will be added to the value. This same listener will
* be removed from the next visible parameter of the same name. If the
* parameter already exists, the listener is added but the value is not
* modified.
*/
public boolean addParameter(String parameterName, Object value,
Iterator iterator, PropertyChangeListener listener) {
return addParameter(parameterName, value, iterator, listener, false);
}
/**
* Add a parameter to the database. This will only add the parameter to the
* first object returned by the given iterator. After the parameter is
* added, the listener will be added to the value. This same listener will
* be removed from the next visible parameter of the same name. If the
* parameter already exists, the listener is added but the value is not
* modified unless overwrite is true.
*/
public boolean addParameter(String parameterName, Object value,
Iterator iterator, PropertyChangeListener listener,
boolean overwrite) {
// Parameter name and value cannot be null.
if (parameterName == null)
throw new IllegalArgumentException("parameterName cannot be null.");
if (value == null)
throw new IllegalArgumentException("value cannot be null.");
// We must process the first object specially. Here we add or
// modify the given parameter.
if (iterator.hasNext()) {
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Create a new entry if it doesn't already exist.
ParameterValue parameterValue = (ParameterValue) database.get(key);
if (parameterValue != null) {
// Add the listener.
if (listener != null)
parameterValue.addPropertyChangeListener(listener);
// Only overwrite the existing value if this has been
// requested.
if (overwrite) {
parameterValue.setValue(value);
firePropertyChange(parameterName, parameterValue);
}
} else {
parameterValue = ParameterValue.createParameterValue(value);
if (listener != null)
parameterValue.addPropertyChangeListener(listener);
database.put(key, parameterValue);
}
} else {
return false;
}
// Loop over the rest of the objects and remove this listener from the
// first entry found.
while (iterator.hasNext()) {
// Create an appropriate ParameterKey.
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate ParameterValue and set it.
ParameterValue parameterValue = (ParameterValue) database.get(key);
if (parameterValue != null) {
parameterValue.removePropertyChangeListener(listener);
return true;
}
}
// Return indicating that the parameter has been added.
return true;
}
/**
* Remove a parameter from the database. This will only remove the parameter
* from the first object returned by the given iterator. After the parameter
* is removed, the listeners from this entry will be added to the next
* visible value.
*/
public boolean removeParameter(String parameterName, Iterator iterator) {
// Parameter name and value cannot be null.
if (parameterName == null)
throw new IllegalArgumentException("parameterName cannot be null.");
// We must process the first object specially. We only
// actually remove the parameter associated with the first
// object.
List movedListeners = null;
if (iterator.hasNext()) {
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Get the associated value.
ParameterValue parameterValue = (ParameterValue) database
.remove(key);
if (parameterValue != null) {
movedListeners = parameterValue.getPropertyChangeListeners();
} else {
return false;
}
} else {
return false;
}
// Loop over the rest of the objects and see if we find another
// version of this parameter. If so, add the listeners to this
// version of the parameter.
if (movedListeners != null && !movedListeners.isEmpty()) {
while (iterator.hasNext()) {
// Create an appropriate ParameterKey.
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
// Extract the appropriate ParameterValue and set it.
ParameterValue parameterValue = (ParameterValue) database
.get(key);
if (parameterValue != null) {
Iterator i = movedListeners.iterator();
while (i.hasNext()) {
PropertyChangeListener listener = (PropertyChangeListener) i
.next();
parameterValue.addPropertyChangeListener(listener);
}
firePropertyChange(parameterName, parameterValue);
return true;
}
}
}
// Return indicating that the parameter has been added.
return true;
}
/**
* Purge a parameter from the list of objects defined by the iterator.
*/
public void purgeParameter(String parameterName, Iterator iterator) {
while (iterator.hasNext()) {
Object object = iterator.next();
ParameterKey key = ParameterKey.createParameterKey(object,
parameterName);
database.remove(key);
}
}
/**
* Purge all entries based on a particular object.
*/
public void purgeEntries(Object object) {
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
database.remove(key);
}
}
/**
* Purge all references to a particular listener.
*/
public void purgePropertyChangeListener(PropertyChangeListener listener) {
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
ParameterValue value = (ParameterValue) database.get(key);
if (value != null) {
value.removePropertyChangeListener(listener);
}
}
}
/**
* Purge all references to a particular listener.
*/
public void purgePropertyChangeListener(Iterator iterator,
PropertyChangeListener listener) {
while (iterator.hasNext()) {
ParameterKey key = (ParameterKey) iterator.next();
ParameterValue value = (ParameterValue) database.get(key);
if (value != null) {
value.removePropertyChangeListener(listener);
}
}
}
/**
* Completely clear this parameter database.
*/
public void clear() {
database.clear();
}
/**
* Write out the entire database as an XML file. Returns true if there were
* no errors generated; returns false otherwise. Note that this simply
* appends a parameters element to the given XMLWriter. The instanceMap must
* map instances of non-Class objects to a unique Integer. The integer value
* is used to tag "local" copies of parameters. If null is passed in as the
* instanceMap none of the local copies of parameters will be saved.
*/
public boolean writeAsXML(XMLWriter xmlWriter, Hashtable instanceMap) {
// Begin the list of parameters.
xmlWriter.openTag("Parameters");
// Get all of the keys in the database and iterate over them.
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
ParameterValue value = (ParameterValue) database.get(key);
if (value != null) {
String type = "";
int id = 0;
boolean classParameter;
boolean valid;
Object obj = key.getObject();
if (obj instanceof Class) {
classParameter = true;
valid = true;
type = ((Class) obj).getName();
} else {
classParameter = false;
if (instanceMap != null) {
Integer instanceId = (Integer) instanceMap.get(obj);
if (instanceId != null) {
valid = true;
id = instanceId.intValue();
} else {
valid = false;
id = 0;
}
} else {
valid = false;
}
}
String val = value.getValue().toString();
String valType = value.getValue().getClass().getName();
// Add the parameter itself.
if (valid) {
if (classParameter) {
xmlWriter.setAttribute("class", type);
xmlWriter.setAttribute("name", key.getName());
xmlWriter.setAttribute("value", val);
xmlWriter.setAttribute("type", valType);
xmlWriter.printTag("ClassParameter");
} else {
xmlWriter.setAttribute("id", id);
xmlWriter.setAttribute("name", key.getName());
xmlWriter.setAttribute("value", val);
xmlWriter.setAttribute("type", valType);
xmlWriter.printTag("InstanceParameter");
}
}
}
}
// Write out the trailing information.
xmlWriter.closeTag();
return true;
}
/**
* Send off the property change event.
*/
public void firePropertyChange(String parameterName,
ParameterValue parameterValue) {
// Now create the property change event.
PropertyChangeEvent pcEvent = new PropertyChangeEvent(this,
parameterName, null, parameterValue.getValue());
// Send off the notification of the new value.
Iterator i = parameterValue.getPropertyChangeListeners().iterator();
while (i.hasNext()) {
PropertyChangeListener listener = (PropertyChangeListener) i.next();
listener.propertyChange(pcEvent);
}
// Send off notification to the database listeners.
i = databaseListeners.iterator();
while (i.hasNext()) {
DatabaseListener listener = (DatabaseListener) i.next();
listener.databaseUpdated();
}
}
/**
* Add a database listener.
*/
public void addDatabaseListener(DatabaseListener listener) {
if (listener != null)
databaseListeners.add(listener);
}
/**
* Remove a database listener
*/
public void removeDatabaseListener(DatabaseListener listener) {
if (listener != null)
databaseListeners.remove(listener);
}
/**
* Get a string array which contains the names of all parameters defined by
* this iterator. Note that all parameters in the database are checked to
* see if they match the given objects.
*/
public String[] getCurrentParameterSet(Iterator iterator) {
TreeSet parameterNames = new TreeSet();
// Make a set of all of the objects in the iterator, so that the
// database itself only needs to be searched once.
HashSet objects = new HashSet();
while (iterator.hasNext()) {
objects.add(iterator.next());
}
// Now loop over all of the keys in the database.
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
if (objects.contains(key.getObject())) {
parameterNames.add(key.getName());
}
}
// Make an array out of the tree set.
String[] names = new String[parameterNames.size()];
names = (String[]) parameterNames.toArray(names);
return names;
}
/**
* Get a string array which gives all of the parameters which are defined
* locally on the given object.
*/
public String[] getLocalParameterSet(Object object) {
TreeSet parameterNames = new TreeSet();
// Iterate over all of the keys in the database.
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
if (object.equals(key.getObject())) {
parameterNames.add(key.getName());
}
}
// Make an array out of the tree set.
String[] names = new String[parameterNames.size()];
names = (String[]) parameterNames.toArray(names);
return names;
}
/**
* Return a hash table in which the keys are the parameter names and the
* values are clones of those values in the database.
*/
public Hashtable cloneLocalParameters(Object object) {
Hashtable clonedParameters = new Hashtable();
// Iterate over all of the keys in the database.
Set keys = database.keySet();
Iterator i = keys.iterator();
while (i.hasNext()) {
ParameterKey key = (ParameterKey) i.next();
if (object.equals(key.getObject())) {
String parameterName = key.getName();
ParameterValue value = (ParameterValue) database.get(key);
Object parameterValue = value.getValue();
try {
Object clonedValue = cloneObject(parameterValue);
clonedParameters.put(parameterName, clonedValue);
} catch (Exception e) {
}
}
}
return clonedParameters;
}
/**
* This will "clone" the given object. This method makes the assumption that
* the objects stored in the database produce a result from the toString()
* method which will then produce an identical object when the single-string
* constructor is called. (Note: this is a general assumption of the
* database as a whole.)
*/
protected Object cloneObject(Object originalObject)
throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
// Setup the call parameters for a constructor which takes a String as
// the only argument.
Object[] parameters = new Object[1];
parameters[0] = originalObject.toString();
Class[] parameterTypes = new Class[1];
parameterTypes[0] = String.class;
// Construct the new value Object based on the given String value.
Class valueClass = originalObject.getClass();
Constructor constructor = valueClass.getConstructor(parameterTypes);
Object newValue = constructor.newInstance(parameters);
return newValue;
}
}