/* $Revision$ $Author$ $Date$
*
* Copyright (C) 1997-2007 Christoph Steinbeck <steinbeck@users.sf.net>
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
package org.openscience.cdk;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.openscience.cdk.event.ChemObjectChangeEvent;
import org.openscience.cdk.interfaces.IChemObject;
import org.openscience.cdk.interfaces.IChemObjectBuilder;
import org.openscience.cdk.interfaces.IChemObjectChangeEvent;
import org.openscience.cdk.interfaces.IChemObjectListener;
/**
* The base class for all chemical objects in this cdk. It provides methods for
* adding listeners and for their notification of events, as well a a hash
* table for administration of physical or chemical properties
*
*@author steinbeck
* @cdk.githash
*@cdk.module data
*/
public class ChemObject implements Serializable, IChemObject, Cloneable
{
/**
* Determines if a de-serialized object is compatible with this class.
*
* This value must only be changed if and only if the new version
* of this class is incompatible with the old version. See Sun docs
* for <a href=http://java.sun.com/products/jdk/1.1/docs/guide
* /serialization/spec/version.doc.html>details</a>.
*/
private static final long serialVersionUID = 2798134548764323328L;
/**
* List for listener administration.
*/
private List<IChemObjectListener> chemObjectListeners;
/**
* A hashtable for the storage of any kind of properties of this IChemObject.
*/
private Map<Object, Object> properties;
/**
* You will frequently have to use some flags on a IChemObject. For example, if
* you want to draw a molecule and see if you've already drawn an atom, or in
* a ring search to check whether a vertex has been visited in a graph
* traversal. Use these flags while addressing particular positions in the
* flag array with self-defined constants (flags[VISITED] = true). 100 flags
* per object should be more than enough.
*/
private boolean[] flags;
/**
* The ID is null by default.
*/
private String identifier;
/**
* Constructs a new IChemObject.
*/
public ChemObject()
{
flags = new boolean[CDKConstants.MAX_FLAG_INDEX + 1];
chemObjectListeners = null;
properties = null;
identifier = null;
}
/**
* Constructs a new IChemObject by copying the flags, and the
* identifier. It does not copy the listeners and properties.
*
* @param chemObject the object to copy
*/
public ChemObject(IChemObject chemObject) {
// copy the flags
boolean[] oldflags = chemObject.getFlags();
flags = new boolean[oldflags.length];
System.arraycopy(oldflags, 0, flags, 0, flags.length);
// copy the identifier
identifier = chemObject.getID();
}
/**
* Lazy creation of chemObjectListeners List.
*
*@return List with the ChemObjects associated.
*/
private List<IChemObjectListener> lazyChemObjectListeners()
{
if (chemObjectListeners == null) {
chemObjectListeners = new ArrayList<IChemObjectListener>();
}
return chemObjectListeners;
}
/**
* Use this to add yourself to this IChemObject as a listener. In order to do
* so, you must implement the ChemObjectListener Interface.
*
*@param col the ChemObjectListener
*@see #removeListener
*/
public void addListener(IChemObjectListener col)
{
List<IChemObjectListener> listeners = lazyChemObjectListeners();
if (!listeners.contains(col))
{
listeners.add(col);
}
// Should we throw an exception if col is already in here or
// just silently ignore it?
}
/**
* Returns the number of ChemObjectListeners registered with this object.
*
*@return the number of registered listeners.
*/
public int getListenerCount() {
if (chemObjectListeners == null) {
return 0;
}
return lazyChemObjectListeners().size();
}
/**
* Use this to remove a ChemObjectListener from the ListenerList of this
* IChemObject. It will then not be notified of change in this object anymore.
*
*@param col The ChemObjectListener to be removed
*@see #addListener
*/
public void removeListener(IChemObjectListener col) {
if (chemObjectListeners == null) {
return;
}
List<IChemObjectListener> listeners = lazyChemObjectListeners();
if (listeners.contains(col)) {
listeners.remove(col);
}
}
/**
* This should be triggered by an method that changes the content of an object
* to that the registered listeners can react to it.
*/
public void notifyChanged() {
if (getNotification() && getListenerCount() > 0) {
List<IChemObjectListener> listeners = lazyChemObjectListeners();
for (Object listener : listeners) {
((IChemObjectListener) listener).stateChanged(
new ChemObjectChangeEvent(this)
);
}
}
}
/**
* This should be triggered by an method that changes the content of an object
* to that the registered listeners can react to it. This is a version of
* notifyChanged() which allows to propagate a change event while preserving
* the original origin.
*
*@param evt A ChemObjectChangeEvent pointing to the source of where
* the change happend
*/
public void notifyChanged(IChemObjectChangeEvent evt) {
if (getNotification() && getListenerCount() > 0) {
List<IChemObjectListener> listeners = lazyChemObjectListeners();
for (Object listener : listeners) {
((IChemObjectListener) listener).stateChanged(evt);
}
}
}
/**
* Lazy creation of properties hash.
*
* @return Returns in instance of the properties
*/
private Map<Object, Object> lazyProperties()
{
if (properties == null)
{
properties = new HashMap<Object, Object>();
}
return properties;
}
/**
* Sets a property for a IChemObject.
*
*@param description An object description of the property (most likely a
* unique string)
*@param property An object with the property itself
*@see #getProperty
*@see #removeProperty
*/
public void setProperty(Object description, Object property)
{
lazyProperties().put(description, property);
notifyChanged();
}
/**
* Removes a property for a IChemObject.
*
*@param description The object description of the property (most likely a
* unique string)
*@see #setProperty
*@see #getProperty
*/
public void removeProperty(Object description)
{
if (properties == null) {
return;
}
lazyProperties().remove(description);
}
/**
* Returns a property for the IChemObject.
*
*@param description An object description of the property (most likely a
* unique string)
*@return The object containing the property. Returns null if
* propert is not set.
*@see #setProperty
*@see #removeProperty
*/
public Object getProperty(Object description)
{
if (properties != null) {
return lazyProperties().get(description);
}
return null;
}
/**
* Returns a Map with the IChemObject's properties.
*
*@return The object's properties as an Hashtable
*@see #setProperties
*/
public Map<Object,Object> getProperties()
{
return lazyProperties();
}
/**
* Clones this <code>IChemObject</code>. It clones the identifier, flags,
* properties and pointer vectors. The ChemObjectListeners are not cloned, and
* neither is the content of the pointer vectors.
*
*@return The cloned object
*/
public Object clone() throws CloneNotSupportedException
{
ChemObject clone = (ChemObject)super.clone();
// clone the flags
clone.flags = new boolean[CDKConstants.MAX_FLAG_INDEX + 1];
System.arraycopy(flags, 0, clone.flags, 0, flags.length);
// clone the properties
if (properties != null) {
Map<Object, Object> clonedHashtable = new HashMap<Object, Object>();
Iterator<Object> keys = properties.keySet().iterator();
while (keys.hasNext()) {
Object key = keys.next();
Object value = properties.get(key);
clonedHashtable.put(key, value);
}
clone.properties = clonedHashtable;
}
// delete all listeners
clone.chemObjectListeners = null;
return clone;
}
/**
* Compares a IChemObject with this IChemObject.
*
*@param object Object of type AtomType
*@return true if the atom types are equal
*/
public boolean compare(Object object)
{
if (!(object instanceof IChemObject))
{
return false;
}
ChemObject chemObj = (ChemObject) object;
return identifier == chemObj.identifier;
}
/**
* Returns the identifier (ID) of this object.
*
*@return a String representing the ID value
*@see #setID
*/
public String getID()
{
return this.identifier;
}
/**
* Sets the identifier (ID) of this object.
*
*@param identifier a String representing the ID value
*@see #getID
*/
public void setID(String identifier)
{
this.identifier = identifier;
notifyChanged();
}
/**
* Sets the value of some flag.
*
*@param flag_type Flag to set
*@param flag_value Value to assign to flag
*@see #getFlag
*/
public void setFlag(int flag_type, boolean flag_value)
{
flags[flag_type] = flag_value;
notifyChanged();
}
/**
* Returns the value of some flag.
*
*@param flag_type Flag to retrieve the value of
*@return true if the flag <code>flag_type</code> is set
*@see #setFlag
*/
public boolean getFlag(int flag_type)
{
return flags[flag_type];
}
/**
* Sets the properties of this object.
*
*@param properties a Hashtable specifying the property values
*@see #getProperties
*/
public void setProperties(Map<Object,Object> properties)
{
Iterator<Object> keys = properties.keySet().iterator();
while (keys.hasNext())
{
Object key = keys.next();
lazyProperties().put(key, properties.get(key));
}
notifyChanged();
}
/**
* Sets the whole set of flags.
*
* @param flagsNew the new flags.
* @see #getFlags
*/
public void setFlags(boolean[] flagsNew){
flags=flagsNew;
}
/**
* Returns the whole set of flags.
*
*@return the flags.
*@see #setFlags
*/
public boolean[] getFlags(){
return(flags);
}
/**
* Clones this <code>IChemObject</code>, but preserves references to <code>Object</code>s.
*
* @return Shallow copy of this IChemObject
* @see #clone
*/
public Object shallowCopy()
{
Object copy = null;
try {
copy = super.clone();
} catch (Exception e) {
e.printStackTrace(System.err);
}
return copy;
}
public IChemObjectBuilder getBuilder() {
return DefaultChemObjectBuilder.getInstance();
}
private boolean doNotification = true;
public void setNotification(boolean bool) {
this.doNotification = bool;
}
public boolean getNotification() {
return this.doNotification;
}
}