/* $RCSfile$
* $Author$
* $Date$
* $Revision$
*
* 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.
*
* 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 org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IBond;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Implements the concept of a covalent bond between two or more atoms. A bond is
* considered to be a number of electrons connecting two or more of atoms.
* <p/>
* It should be noted that the majority of applications will consider 2-center bonds,
* especially since the bond orders currently supported are really only valid for
* 2-center bonds. However the code does support multi-center bonds, though the
* orders may not make sense at this point.
* <p/>
* In general code that assumes bonds are 2-centered can use this class seamlessly, as
* the semantics are identical to the older versions. Care shoud be exercised when
* using multi-center bonds using this class as the orders may not make sense.
*
* @author steinbeck
* @cdk.module data
* @cdk.githash
* @cdk.created 2003-10-02
* @cdk.keyword bond
* @cdk.keyword atom
* @cdk.keyword electron
*/
public class Bond extends ElectronContainer implements IBond, Serializable, Cloneable {
/**
* Determines if a de-serialized object is compatible with this class.
* <p/>
* 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 = 7057060562283387384L;
/**
* The bond order of this bond.
*/
protected IBond.Order order = (Order) CDKConstants.UNSET;
/**
* Number of atoms contained by this object.
*/
protected int atomCount = 0;
/**
* A list of atoms participating in this bond.
*/
protected IAtom[] atoms = null;
/**
* A descriptor the stereochemical orientation of this bond.
*/
protected IBond.Stereo stereo;
/**
* Constructs an empty bond.
*/
public Bond() {
this(null, null, null, IBond.Stereo.NONE);
}
/**
* Constructs a bond with a single bond order..
*
* @param atom1 the first Atom in the bond
* @param atom2 the second Atom in the bond
*/
public Bond(IAtom atom1, IAtom atom2) {
this(atom1, atom2, IBond.Order.SINGLE, IBond.Stereo.NONE);
}
/**
* Constructs a bond with a given order.
*
* @param atom1 the first Atom in the bond
* @param atom2 the second Atom in the bond
* @param order the bond order
*/
public Bond(IAtom atom1, IAtom atom2, Order order) {
this(atom1, atom2, order, IBond.Stereo.NONE);
}
/**
* Constructs a multi-center bond, with undefined order and no stereo information.
*
* @param atoms An array of IAtom containing the atoms constituting the bond
*/
public Bond(IAtom[] atoms) {
this.atoms = new IAtom[atoms.length];
System.arraycopy(atoms, 0, this.atoms, 0, atoms.length);
atomCount = this.atoms.length;
}
/**
* Constructs a multi-center bond, with a specified order and no stereo information.
*
* @param atoms An array of IAtom containing the atoms constituting the bond
* @param order The order of the bond
*/
public Bond(IAtom[] atoms, Order order) {
this.atoms = new IAtom[atoms.length];
System.arraycopy(atoms, 0, this.atoms, 0, atoms.length);
atomCount = this.atoms.length;
this.order = order;
}
/**
* Constructs a bond with a given order and stereo orientation from an array
* of atoms.
*
* @param atom1 the first Atom in the bond
* @param atom2 the second Atom in the bond
* @param order the bond order
* @param stereo a descriptor the stereochemical orientation of this bond
*/
public Bond(IAtom atom1, IAtom atom2, Order order, IBond.Stereo stereo) {
atoms = new Atom[2];
atoms[0] = atom1;
atoms[1] = atom2;
this.order = order;
this.stereo = stereo;
this.atomCount = 2;
}
/**
* Returns the Iterator to atoms making up this bond.
* Iterator.remove() is not implemented.
*
* @return An Iterator to atoms participating in this bond
* @see #setAtoms
*/
public Iterable<IAtom> atoms() {
return new Iterable<IAtom>() {
public Iterator<IAtom> iterator() {
return new AtomsIterator();
}
};
}
/**
* The inner Iterator class.
*/
private class AtomsIterator implements Iterator<IAtom> {
private int pointer = 0;
public boolean hasNext() {
return pointer < atomCount;
}
public IAtom next() {
++pointer;
return atoms[pointer - 1];
}
public void remove() {
}
}
/**
* Sets the array of atoms making up this bond.
*
* @param atoms An array of atoms that forms this bond
* @see #atoms
*/
public void setAtoms(IAtom[] atoms) {
this.atoms = atoms;
atomCount = atoms.length;
notifyChanged();
}
/**
* Returns the number of Atoms in this Bond.
*
* @return The number of Atoms in this Bond
*/
public int getAtomCount() {
return atomCount;
}
/**
* Returns an Atom from this bond.
*
* @param position The position in this bond where the atom is
* @return The atom at the specified position, null if there are no atoms in the bond
* @see #setAtom
*/
public IAtom getAtom(int position) {
if (atoms == null) return null;
else return atoms[position];
}
/**
* Returns the atom connected to the given atom.
* <p/>
* This method is only strictly relevant for 2-center bonds
* since in multi-center bonds, a given atom will be connected
* to multiple atoms.
* <p/>
* If called for a multi-center bond, then the next atom in the
* atom list is returned. This is probably not what is expected and
* hence the user should instead call
* {@link #getConnectedAtoms(org.openscience.cdk.interfaces.IAtom)}
*
* @param atom The atom the bond partner is searched of
* @return the connected atom or null if the atom is not part of the bond
* @see #getConnectedAtoms(org.openscience.cdk.interfaces.IAtom)
*/
public IAtom getConnectedAtom(IAtom atom) {
if (atoms[0] == atom) {
return atoms[1];
} else if (atoms[1] == atom) {
return atoms[0];
}
return null;
}
/**
* Returns all the atoms in the bond connected to the specified atom.
* <p/>
* Though this can be used for traditional 2-center bonds, it is oriented
* towards multi-center bonds, where a single atom is connected to multiple
* atoms.
*
* @param atom The atom whose partners are to be searched for
* @return An array of the connected atoms, null if the atom is not part of the bond
* @see #getConnectedAtom(org.openscience.cdk.interfaces.IAtom)
*/
public IAtom[] getConnectedAtoms(IAtom atom) {
boolean atomIsInBond = false;
for (IAtom localAtom : atoms) {
if (localAtom == atom) {
atomIsInBond = true;
break;
}
}
if (!atomIsInBond) return null;
List<IAtom> conAtoms = new ArrayList<IAtom>();
for (IAtom localAtom : atoms) {
if (localAtom != atom) conAtoms.add(localAtom);
}
return conAtoms.toArray(new IAtom[]{});
}
/**
* Returns true if the given atom participates in this bond.
*
* @param atom The atom to be tested if it participates in this bond
* @return true if the atom participates in this bond
*/
public boolean contains(IAtom atom) {
if (atoms == null) return false;
for (IAtom localAtom : atoms) {
if (localAtom == atom) return true;
}
return false;
}
/**
* Sets an Atom in this bond.
*
* @param atom The atom to be set
* @param position The position in this bond where the atom is to be inserted
* @see #getAtom
*/
public void setAtom(IAtom atom, int position) {
atoms[position] = atom;
notifyChanged();
}
/**
* Returns the bond order of this bond.
*
* @return The bond order of this bond
* @see org.openscience.cdk.CDKConstants org.openscience.cdk.CDKConstants
* for predefined values.
* @see #setOrder
*/
public Order getOrder() {
return this.order;
}
/**
* Sets the bond order of this bond.
*
* @param order The bond order to be assigned to this bond
* @see org.openscience.cdk.CDKConstants
* org.openscience.cdk.CDKConstants for predefined values.
* @see #getOrder
*/
public void setOrder(Order order) {
this.order = order;
notifyChanged();
}
/**
* Returns the stereo descriptor for this bond.
*
* @return The stereo descriptor for this bond
* @see #setStereo
* @see org.openscience.cdk.CDKConstants for predefined values.
*/
public IBond.Stereo getStereo() {
return this.stereo;
}
/**
* Sets the stereo descriptor for this bond.
*
* @param stereo The stereo descriptor to be assigned to this bond.
* @see #getStereo
* @see org.openscience.cdk.CDKConstants for predefined values.
*/
public void setStereo(IBond.Stereo stereo) {
this.stereo = stereo;
notifyChanged();
}
/**
* Returns the geometric 2D center of the bond.
*
* @return The geometric 2D center of the bond
*/
public Point2d get2DCenter() {
double xOfCenter = 0;
double yOfCenter = 0;
for (IAtom atom : atoms) {
xOfCenter += atom.getPoint2d().x;
yOfCenter += atom.getPoint2d().y;
}
return new Point2d(xOfCenter / ((double) getAtomCount()),
yOfCenter / ((double) getAtomCount()));
}
/**
* Returns the geometric 3D center of the bond.
*
* @return The geometric 3D center of the bond
*/
public Point3d get3DCenter() {
double xOfCenter = 0;
double yOfCenter = 0;
double zOfCenter = 0;
for (IAtom atom : atoms) {
xOfCenter += atom.getPoint3d().x;
yOfCenter += atom.getPoint3d().y;
zOfCenter += atom.getPoint3d().z;
}
return new Point3d(xOfCenter / getAtomCount(),
yOfCenter / getAtomCount(),
zOfCenter / getAtomCount());
}
/**
* Compares a bond with this bond.
*
* @param object Object of type Bond
* @return true if the bond is equal to this bond
*/
public boolean compare(Object object) {
if (object instanceof IBond) {
Bond bond = (Bond) object;
for (IAtom atom : atoms) {
if (!bond.contains(atom)) {
return false;
}
}
// not important ??!!
//if (order==bond.order)
// return false;
return true;
}
return false;
}
/**
* Checks whether a bond is connected to another one.
* This can only be true if the bonds have an Atom in common.
*
* @param bond The bond which is checked to be connect with this one
* @return true if the bonds share an atom, otherwise false
*/
public boolean isConnectedTo(IBond bond) {
for (IAtom atom : atoms) {
if (bond.contains(atom)) return true;
}
return false;
}
/**
* Clones this bond object, including clones of the atoms between which the
* bond is defined.
*
* @return The cloned object
*/
public Object clone() throws CloneNotSupportedException {
Bond clone = (Bond) super.clone();
// clone all the Atoms
if (atoms != null) {
clone.atoms = new IAtom[atoms.length];
for (int f = 0; f < atoms.length; f++) {
if (atoms[f] != null) {
clone.atoms[f] = (IAtom) ((IAtom) atoms[f]).clone();
}
}
}
return clone;
}
/**
* Returns a one line string representation of this Container. This method is
* conform RFC #9.
*
* @return The string representation of this Container
*/
public String toString() {
StringBuffer resultString = new StringBuffer(32);
resultString.append("Bond(").append(this.hashCode());
if (getOrder() != null) {
resultString.append(", #O:").append(getOrder());
}
resultString.append(", #S:").append(getStereo());
if (getAtomCount() > 0) {
resultString.append(", #A:").append(getAtomCount());
for (int i = 0; i < atomCount; i++) {
resultString.append(", ").append("" + atoms[i]);
}
}
resultString.append(", ").append(super.toString());
resultString.append(')');
return resultString.toString();
}
}