/*
* EuroCarbDB, a framework for carbohydrate bioinformatics
*
* Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
* A copy of this license accompanies this distribution in the file LICENSE.txt.
*
* 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.
*
* Last commit: $Rev: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$
*/
package org.eurocarbdb.application.glycanbuilder;
import java.util.*;
import java.util.regex.*;
/**
This object represent a molecule as a collection of atoms and
charges.
@author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class Molecule {
private static Pattern mol_pattern;
static {
mol_pattern = Pattern.compile("([A-Z][a-z]?)([0-9]*)");
}
private TreeMap<Atom,Integer> atoms;
private double main_mass;
private double avg_mass;
private int charges;
/**
Create an empty molecule.
*/
public Molecule() {
atoms = new TreeMap<Atom,Integer>();
main_mass = 0.;
avg_mass = 0.;
charges = 0;
}
/**
Initialize a molecule from its chemical formula.
@param init the chemical formula
@throws Exception if the formula cannot be parsed
*/
public Molecule(String init) throws Exception {
atoms = new TreeMap<Atom,Integer>();
main_mass = 0.;
avg_mass = 0.;
charges = 0;
if( init.equals("0") )
init = "";
int mul = 1;
if( init.startsWith("-") ) {
mul = -1;
init = init.substring(1);
}
int cur = 0;
Matcher m = mol_pattern.matcher(init);
while( m.find() ) {
String atom_name = m.group(1);
String atom_number_str = m.group(2);
if( atom_number_str!=null && atom_number_str.length()>0 )
add(atom_name,mul*Integer.valueOf(atom_number_str));
else
add(atom_name,mul);
cur = m.end();
}
int i=cur;
for( ; i<init.length(); i++ ) {
if( init.charAt(i)=='-' )
charges--;
else if( init.charAt(i)=='+' )
charges++;
else
break;
}
if( i!=init.length() )
throw new Exception("Invalid format: " + init);
if( charges>0 ) {
main_mass -= charges * MassUtils.electron.getMainMass();
avg_mass -= charges * MassUtils.electron.getAverageMass();
}
}
/**
Return <code>true</code> if the two molecules have the same
chemical formula.
*/
public boolean equals(Object other) {
if( other==null || !(other instanceof Molecule) )
return false;
return this.toString().equals(other.toString());
}
/**
Return an hash code associated with this molecule.
*/
public int hashCode() {
return this.toString().hashCode();
}
/**
Create a new object which is a copy of the current one.
*/
public Molecule clone() {
Molecule ret = new Molecule();
ret.atoms = (TreeMap<Atom,Integer>)this.atoms.clone();
ret.main_mass = this.main_mass;
ret.avg_mass = this.avg_mass;
ret.charges = this.charges;
return ret;
}
/**
Return a collection of entries representing the atoms in this
molecule and their quantities.
*/
public Collection<Map.Entry<Atom,Integer>> getAtoms() {
return atoms.entrySet();
}
/**
Return the mass of the molecule in the current settings.
*/
public double getMass() {
return main_mass;
}
/**
Return the mono-isotopic mass of the molecule.
*/
public double getMainMass() {
return main_mass;
}
/**
Return the mono-isotopic mass of the molecule.
*/
public double getAverageMass() {
return avg_mass;
}
/**
Return the mass-to-charge ratio of the molecule.
*/
public double getMZ() {
if( charges==0 )
return getMass();
return getMass()/Math.abs(charges);
}
/**
Return the number of positive charges associated to this
molecule.
*/
public int getNoCharges() {
return charges;
}
/**
Add <code>num</code> positive charges to the molecule.
*/
public void addPositiveCharges(int num) {
charges += num;
main_mass -= num * MassUtils.electron.getMainMass();
avg_mass -= num * MassUtils.electron.getAverageMass();
}
/**
Remove <code>num</code> positive charges from the molecule.
*/
public void removePositiveCharges(int num) {
addNegativeCharges(num);
}
/**
Add <code>num</code> negative charges to the molecule.
*/
public void addNegativeCharges(int num) {
charges -= num;
main_mass += num * MassUtils.electron.getMainMass();
avg_mass += num * MassUtils.electron.getAverageMass();
}
/**
Remove <code>num</code> negative charges to the molecule.
*/
public void removeNegativeCharges(int num) {
addPositiveCharges(num);
}
/**
Clone the molecule and add <code>num</code> atoms of type
<code>atom</code> to it.
*/
public Molecule and(String atom, int num) throws Exception {
Molecule ret = this.clone();
ret.add(atom,num);
return ret;
}
/**
Clone the molecule and add the atom <code>a</code> to it.
*/
public Molecule and(Atom a) {
return this.and(a,1);
}
/**
Clone the molecule and add <code>num</code> instances of the
atom <code>a</code> to it.
*/
public Molecule and(Atom a, int num) {
Molecule ret = this.clone();
ret.add(a,num);
return ret;
}
/**
Clone the molecule and add the content of molecule
<code>m</code> to it.
*/
public Molecule and(Molecule m) {
return this.and(m,1);
}
/**
Clone the molecule and add <code>num</code> times the content
of molecule <code>m</code> to it.
*/
public Molecule and(Molecule m, int num) {
Molecule ret = this.clone();
ret.add(m,num);
return ret;
}
/**
Add one atom of type <code>atom</code> to the molecule.
*/
public void add(String atom) throws Exception {
add(MassUtils.getAtom(atom),1);
}
/**
Add <code>num</code> atoms of type <code>atom</code> to the
molecule.
*/
public void add(String atom, int num) throws Exception {
add(MassUtils.getAtom(atom),num);
}
/**
Add one instance of atom <code>a</code> to the molecule.
*/
public void add(Atom a) {
add(a,1);
}
/**
Add <code>num</code> instances of atom <code>a</code> to the
molecule.
*/
public void add(Atom a, int num) {
Integer cur_num = atoms.get(a);
if( cur_num==null )
atoms.put(a,num);
else
atoms.put(a,cur_num+num);
main_mass += num * a.getMainMass();
avg_mass += num * a.getAverageMass();
}
/**
Add the content of molecule <code>m</code> to the molecule.
*/
public void add(Molecule m) {
add(m,1);
}
/**
Add <code>num</code> times the content of molecule
<code>m</code> to the molecule.
*/
public void add(Molecule m, int num) {
if( m!=null ) {
for( Map.Entry<Atom,Integer> a : m.atoms.entrySet() )
this.add(a.getKey(),num*a.getValue());
this.addPositiveCharges(num*m.charges);
}
}
/**
Remove <code>num</code> atoms of type <code>atom</code> from
the molecule.
*/
public void remove(String atom, int num) throws Exception {
add(atom,-num);
}
/**
Remove one instance of the atom <code>a</code> from the
molecule.
*/
public void remove(Atom a) {
add(a,-1);
}
/**
Remove <code>num</code> instances of atom <code>a</code> from the
molecule.
*/
public void remove(Atom a, int num) {
add(a,-num);
}
/**
Remove the content of molecule <code>m</code> from the
molecule.
*/
public void remove(Molecule m) {
add(m,-1);
}
/**
Remove <code>num</code> times the content of molecule
<code>m</code> from the molecule.
*/
public void remove(Molecule m, int num) {
add(m,-num);
}
/**
Return the chemical formula of the molecule.
*/
public String toString() {
StringBuilder sb = new StringBuilder();
for( Map.Entry<Atom,Integer> a : atoms.entrySet() ) {
sb.append(a.getKey().toString());
sb.append(a.getValue().toString());
}
for( int i=0; i<Math.abs(charges); i++ ) {
if( charges>0 )
sb.append('+');
else
sb.append('-');
}
return sb.toString();
}
}