/*
* 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.glycoworkbench;
import org.eurocarbdb.application.glycanbuilder.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import java.util.TreeSet;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import javax.xml.transform.sax.TransformerHandler;
import org.xml.sax.helpers.AttributesImpl;
/**
Contain a list of annotated peaks in a mass spectrum, where the
annotations derive from a single structure (intact or fragment
molecule)
@author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class PeakAnnotationCollection implements SAXUtils.SAXWriter {
private double max_intensity = 0.;
private Vector<PeakAnnotation> peak_annotations;
/**
Create an empty list of peak annotations
*/
public PeakAnnotationCollection() {
max_intensity = 0.;
peak_annotations = new Vector<PeakAnnotation>();
}
/**
Create a copy of the current object
*/
public PeakAnnotationCollection clone() {
PeakAnnotationCollection ret = new PeakAnnotationCollection();
for( PeakAnnotation pa : peak_annotations )
ret.addPeakAnnotation(pa);
return ret;
}
/**
Remove all peak annotations from the list
*/
public void clear() {
max_intensity = 0.;
peak_annotations.clear();
}
/**
Remove all annotations for a specific peak, leave the peak in
the list
*/
public void clearAnnotations(Peak p) {
if( p==null )
return;
for( Iterator<PeakAnnotation> i=peak_annotations.iterator(); i.hasNext(); ) {
PeakAnnotation pa = i.next();
if( p.equals(pa.getPeak()) )
i.remove();
}
addPeakAnnotation(p);
}
/**
Remove all annotations for a collection of peaks
*/
public void clearAnnotations(Collection<Peak> peaks) {
TreeSet<Peak> removed = new TreeSet<Peak>();
for( Iterator<PeakAnnotation> i=peak_annotations.iterator(); i.hasNext(); ) {
PeakAnnotation pa = i.next();
for( Peak p : peaks ) {
if( p.equals(pa.getPeak()) ) {
removed.add(pa.getPeak());
i.remove();
break;
}
}
}
for( Peak p : removed )
addPeakAnnotation(p);
}
/**
Return the list of peak annotations
*/
public Vector<PeakAnnotation> getPeakAnnotations() {
return peak_annotations;
}
/**
Return all annotations for a specific peak
*/
public Vector<PeakAnnotation> getPeakAnnotations(Peak p) {
Vector<PeakAnnotation> ret = new Vector<PeakAnnotation>();
for( PeakAnnotation pa : peak_annotations ) {
if( pa.getPeak().equals(p) )
ret.add(pa);
}
return ret;
}
/**
Return all annotations for a specific mass/charge value
*/
public Collection<PeakAnnotation> getPeakAnnotations(double mz_ratio) {
double mz_tol = 0.000001;
Vector<PeakAnnotation> ret = new Vector<PeakAnnotation>();
for( PeakAnnotation pa : peak_annotations ) {
if( pa.getPeak().getMZ()>mz_ratio+mz_tol )
return ret;
if( pa.getPeak().getMZ()>mz_ratio-mz_tol )
ret.add(pa);
}
return ret;
}
/**
Return <code>true</code> if the list contain the requested peak
annotation
*/
public boolean contains(PeakAnnotation pa) {
return peak_annotations.contains(pa);
}
/**
Return an iterator over the list of annotations
*/
public Iterator<PeakAnnotation> iterator() {
return peak_annotations.iterator();
}
/**
Return the peak annotation at the specified position in the
list
*/
public PeakAnnotation elementAt(int ind) {
return peak_annotations.elementAt(ind);
}
/**
Return the number of peak annotations
*/
public int size() {
return peak_annotations.size();
}
/**
Return <code>true</code> if all peaks are annotated with intact
glycan molecules
*/
public boolean isProfile() {
for( PeakAnnotation pa : peak_annotations )
if( pa.getFragment()!=null && pa.getFragment().isFragment() )
return false;
return true;
}
/**
Add a new peak with no annotations to the list
@return <code>true</code> if the peak was added to the
list and no duplicates were present
*/
public boolean addPeakAnnotation(Peak p) {
if( p==null )
return false;
return addPeakAnnotation(new PeakAnnotation(p));
}
/**
Add a new peak annotation to the list
@param f the annotation
@return <code>true</code> if the annotation was added to the
list and no duplicates were present
*/
public boolean addPeakAnnotation(Peak p, FragmentEntry f) {
if( p==null )
return false;
return addPeakAnnotation(new PeakAnnotation(p,f));
}
/**
Add a new peak annotation to the list
@param f the annotation
@param c the charges associated with this annotation
@return <code>true</code> if the annotation was added to the
list and no duplicates were present
*/
public boolean addPeakAnnotation(Peak p, FragmentEntry f, IonCloud c) {
if( p==null )
return false;
return addPeakAnnotation(new PeakAnnotation(p,f,c));
}
/**
Add a new peak annotation to the list
@param f the annotation
@param c the charges associated with this annotation
@param e the exchanges associated with this annotation
@return <code>true</code> if the annotation was added to the
list and no duplicates were present
*/
public boolean addPeakAnnotation(Peak p, FragmentEntry f, IonCloud c, IonCloud e) {
if( p==null )
return false;
return addPeakAnnotation(new PeakAnnotation(p,f,c,e));
}
/**
Add a new peak annotation to the list
@return <code>true</code> if the annotation was added to the
list and no duplicates were present
*/
public boolean addPeakAnnotation(PeakAnnotation toadd) {
if( toadd==null )
return false;
// update max intensity
max_intensity = Math.max(max_intensity,toadd.getPeak().getIntensity());
// add sorted (start from the end because the peaks are tested in increasing order)
for( int i=peak_annotations.size()-1; i>=0; i-- ) {
PeakAnnotation cur = peak_annotations.elementAt(i);
if( !toadd.isAnnotated() && cur.getPeak().equals(toadd.getPeak()) )
return false; // empty annotation not needed
int comparison = cur.compareTo(toadd);
if( comparison<0 ) {
peak_annotations.insertElementAt(toadd,i+1);
if( !cur.isAnnotated() && cur.getPeak().equals(toadd.getPeak()) ) {
// remove empty annotation
peak_annotations.removeElementAt(i);
}
return true;
}
if( comparison==0 )
return false;
}
peak_annotations.insertElementAt(toadd,0);
return true;
}
/**
Add all the annotations from a collection to the list
@return <code>true</code> if at least one annotation from the
collection was added
*/
public boolean addPeakAnnotations(PeakAnnotationCollection pac) {
if( pac==null )
return false;
boolean added = false;
for( int l=pac.peak_annotations.size()-1, i=peak_annotations.size()-1; l>=0; l-- ) {
PeakAnnotation toadd = pac.peak_annotations.elementAt(l);
// update max intensity
max_intensity = Math.max(max_intensity,toadd.getPeak().getIntensity());
// find position
int comparison = 0;
PeakAnnotation cur = null;
for( ; i>=0; i-- ) {
cur = peak_annotations.elementAt(i);
if( !toadd.isAnnotated() && cur.getPeak().equals(toadd.getPeak()) ) {
comparison = 0;
break;
}
if( (comparison = cur.compareTo(toadd))<=0 )
break; // position found
}
// add sorted
if( i<0 || comparison<0 ) {
peak_annotations.insertElementAt(toadd,i+1);
if( i>=0 && !cur.isAnnotated() && cur.getPeak().equals(toadd.getPeak()) ) {
peak_annotations.removeElementAt(i); // remove empty annotation
i--;
}
added = true;
}
}
return added;
}
/**
Remove all annotations from the specified peak, and also remove
the peak from the list
@return <code>true</code> if at leas one annotation was removed
*/
public boolean removeAllPeakAnnotations(Peak p) {
boolean removed = false;
for( int i=0; i<peak_annotations.size(); i++ ) {
PeakAnnotation pa = peak_annotations.elementAt(i);
if( pa.getPeak().compareTo(p)>0 )
return removed;
if( pa.getPeak().equals(p) ) {
peak_annotations.removeElementAt(i);
i--;
}
}
return removed;
}
/**
Remove a specific peak annotation from the list
@param leave_empty if <code>true</code> leave the non annotated
peak in the list
@return the index of the removed annotation
*/
public int removePeakAnnotation(PeakAnnotation pa, boolean leave_empty) {
if( pa==null )
return -1;
// add sorted
for( int i=0; i<peak_annotations.size(); i++ ) {
PeakAnnotation e = peak_annotations.elementAt(i);
if( e.equals(pa) ) {
// remove annotation
peak_annotations.removeElementAt(i);
if( (i>0 && peak_annotations.elementAt(i-1).getPeak().equals(pa.getPeak())) ||
(i<peak_annotations.size() && peak_annotations.elementAt(i).getPeak().equals(pa.getPeak())) )
return i;
if( leave_empty ) {
// add empty annotation
peak_annotations.insertElementAt(new PeakAnnotation(pa.getPeak()),i);
}
return i;
}
}
return -1;
}
/**
Recalculate the maximum intensity of all peaks after a change
in the peak intensities
*/
public void updateIntensities() {
max_intensity = 0.;
for( PeakAnnotation pa : peak_annotations )
max_intensity = Math.max(max_intensity,pa.getPeak().getIntensity());
}
/**
Return <code>true</code> if the specified peak is annotated
*/
public boolean isAnnotated(Peak p) {
for( PeakAnnotation pa : peak_annotations )
if( pa.getPeak().equals(p) )
return true;
return false;
}
/**
Equal to {@link #elementAt}
*/
public PeakAnnotation getPeakAnnotation(int ind) {
return elementAt(ind);
}
/**
Return the index of the first annotation for the peak with the
specified mass/charge value
*/
public int indexOf(double mz) {
int ind = 0;
double last_err = Double.POSITIVE_INFINITY;
for( PeakAnnotation pa : peak_annotations ) {
double err = Math.abs(mz - pa.getPeak().getMZ());
if( err > last_err )
return ind-1;
last_err = err;
ind++;
}
return ind-1;
}
/**
Return the index of the specified annotation
*/
public int indexOf(PeakAnnotation pa) {
return peak_annotations.indexOf(pa);
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
*/
public double[][] getPeakData() {
return getPeakData(0.,false);
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
@param rel_int output relative instead of absolute intensities,
normalize by the maximum intensity in the collection
*/
public double[][] getPeakData(boolean rel_int) {
return getPeakData(0.,rel_int);
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
@param add_boundaries add two peaks at beginning and end of the
table whose mass/charge values differs from their neighbours of
the specified value
@param rel_int output relative instead of absolute intensities,
normalize by the maximum intensity in the collection
*/
public double[][] getPeakData(double add_boundaries, boolean rel_int) {
// init vectors
int no_peaks = size();
int added = (add_boundaries>0.) ?2 :0;
double[][] ret = new double[2][];
ret[0] = new double[no_peaks+added];
ret[1] = new double[no_peaks+added];
// copy peaks
double div = 1.;
if( rel_int )
div = getMaxIntensity()/100.;
for( int i=0; i<no_peaks; i++ ) {
Peak p = elementAt(i).getPeak();
ret[0][i+added/2] = p.getMZ();
ret[1][i+added/2] = p.getIntensity()/div;
}
// add boundaries
if( add_boundaries>0. ) {
ret[0][0] = ret[0][1]-add_boundaries;
ret[1][0] = 0.;
ret[0][no_peaks+1] = ret[0][no_peaks]+add_boundaries;
ret[1][no_peaks+1] = 0.;
}
return ret;
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
@param from_mz limit the minimum mass/charge value of the peaks
to be output
@param to_mz limit the maximum mass/charge value of the peaks
to be output
*/
public double[][] getPeakData(double from_mz, double to_mz) {
return getPeakData(from_mz,to_mz,0.,false);
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
@param from_mz limit the minimum mass/charge value of the peaks
to be output
@param to_mz limit the maximum mass/charge value of the peaks
to be output
@param rel_int output relative instead of absolute intensities,
normalize by the maximum intensity in the collection
*/
public double[][] getPeakData(double from_mz, double to_mz, boolean rel_int) {
return getPeakData(from_mz,to_mz,0.,rel_int);
}
/**
Return the list of peaks in the collection as a 2xN table of
mass/charge and intensity values
@param from_mz limit the minimum mass/charge value of the peaks
to be output
@param to_mz limit the maximum mass/charge value of the peaks
to be output
@param add_boundaries add two peaks at beginning and end of the
table whose mass/charge values differs from their neighbours of
the specified value
@param rel_int output relative instead of absolute intensities,
normalize by the maximum intensity in the collection
*/
public double[][] getPeakData(double from_mz, double to_mz, double add_boundaries, boolean rel_int) {
// count peaks
int no_peaks = 0;
for( PeakAnnotation pa : peak_annotations ) {
if( pa.getPeak().getMZ()>=from_mz && pa.getPeak().getMZ()<=to_mz )
no_peaks++;
}
// init vectors
int added = (add_boundaries>0.) ?2 :0;
double[][] ret = new double[2][];
ret[0] = new double[no_peaks+added];
ret[1] = new double[no_peaks+added];
// copy peaks
double div = 1.;
if( rel_int )
div = getMaxIntensity(from_mz,to_mz)/100.;
int i=0;
for( PeakAnnotation pa : peak_annotations ) {
Peak p = pa.getPeak();
if( pa.getPeak().getMZ()>=from_mz && pa.getPeak().getMZ()<=to_mz ) {
ret[0][i+added/2] = p.getMZ();
ret[1][i+added/2] = p.getIntensity()/div;
i++;
}
}
// add boundaries
if( add_boundaries>0. ) {
ret[0][0] = ret[0][1]-add_boundaries;
ret[1][0] = 0.;
ret[0][no_peaks+1] = ret[0][no_peaks]+add_boundaries;
ret[1][no_peaks+1] = 0.;
}
return ret;
}
/**
Return the peak associated with the peak annotation at the specified
index
*/
public Peak getPeak(int ind) {
return elementAt(ind).getPeak();
}
/**
Return the annotation associated with the peak annotation at
the specified index
*/
public Annotation getAnnotation(int ind) {
return elementAt(ind).getAnnotation();
}
/**
Return the annotation associated with the peak annotation at
the specified index
*/
public FragmentEntry getFragmentEntry(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry();
}
/**
Return the charges associated with the peak annotation at
the specified index
*/
public IonCloud getIons(int ind) {
return elementAt(ind).getAnnotation().getIons();
}
/**
Return the neutral exchanges associated with the peak annotation at
the specified index
*/
public IonCloud getNeutralExchanges(int ind) {
return elementAt(ind).getAnnotation().getNeutralExchanges();
}
/**
Return the mass/charge value of the peak associated with the
peak annotation at the specified index
*/
public double getMZ(int ind) {
return elementAt(ind).getPeak().getMZ();
}
/**
Return the intensity of the peak associated with the peak
annotation at the specified index
*/
public double getIntensity(int ind) {
return elementAt(ind).getPeak().getIntensity();
}
/**
Return the relative intensity of the peak associated with the
peak annotation at the specified index, normalize the value by
the maximum intensity of the peaks in the collection
*/
public double getRelativeIntensity(int ind) {
if( max_intensity == 0. )
return getIntensity(ind);
return 100.*getIntensity(ind)/max_intensity;
}
/**
Return the maximum value of the intensity of the peaks in the
collection
*/
public double getMaxIntensity() {
return max_intensity;
}
/**
Return the maximum value of the intensity of the peaks in the
collection
@param from_mz limit the minimum mass/charge value of the peaks
for which the maximum intensity must be computed
@param to_mz limit the maximum mass/charge value of the peaks
for which the maximum intensity must be computed
*/
public double getMaxIntensity(double from_mz, double to_mz) {
double ret = 0.;
for( PeakAnnotation pa : peak_annotations ) {
if( pa.getPeak().getMZ()>=from_mz && pa.getPeak().getMZ()<=to_mz )
ret = Math.max(pa.getPeak().getIntensity(),ret);
}
return ret;
}
/**
Return the minimum difference between experimental and
predicted mass/charge values
*/
public double getMinAccuracy() {
double min_acc = Double.MAX_VALUE;
for( PeakAnnotation pa : peak_annotations) {
if( pa.isAnnotated() )
min_acc = Math.min(pa.getAccuracy(),min_acc);
}
return min_acc;
}
/**
Return the maximum difference between experimental and
predicted mass/charge values
*/
public double getMaxAccuracy() {
double max_acc = Double.MIN_VALUE;
for( PeakAnnotation pa : peak_annotations) {
if( pa.isAnnotated() )
max_acc = Math.max(pa.getAccuracy(),max_acc);
}
return max_acc;
}
/**
Return the minimum difference between experimental and
predicted mass/charge values expressed in PPM
*/
public double getMinAccuracyPPM() {
double min_acc = Double.MAX_VALUE;
for( PeakAnnotation pa : peak_annotations) {
if( pa.isAnnotated() )
min_acc = Math.min(pa.getAccuracyPPM(),min_acc);
}
return min_acc;
}
/**
Return the maximum difference between experimental and
predicted mass/charge values expressed in PPM
*/
public double getMaxAccuracyPPM() {
double max_acc = Double.MIN_VALUE;
for( PeakAnnotation pa : peak_annotations) {
if( pa.isAnnotated() )
max_acc = Math.max(pa.getAccuracyPPM(),max_acc);
}
return max_acc;
}
/**
Return the difference between experimental and predicted
mass/charge values for the peak annotation at the specified
position
*/
public double getAccuracy(int ind) {
if( !isAnnotated(ind) )
return 0.;
return elementAt(ind).getAccuracy();
}
/**
Return the difference in PPM between experimental and predicted
mass/charge values for the peak annotation at the specified
position
*/
public double getAccuracyPPM(int ind) {
if( !isAnnotated(ind) )
return 0.;
return elementAt(ind).getAccuracyPPM();
}
/**
Return <code>true</code> if the peak annotation at the
specified position contains a non-empty structure
*/
public boolean isAnnotated(int ind) {
return elementAt(ind).isAnnotated();
}
/**
Return the glycan structure associated with the peak annotation
at the specified position
@see FragmentEntry#getFragment
*/
public Glycan getFragment(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry().getFragment();
}
/**
Return the type of fragment associated with the peak annotation
at the specified position
@see FragmentEntry#getName
*/
public String getFragmentType(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry().getName();
}
/**
Return the score associated with the peak annotation at the
specified position
@see FragmentEntry#getScore
*/
public double getFragmentScore(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry().getScore();
}
/**
Return the mass of the fragment associated with the peak annotation
at the specified position
@see FragmentEntry#getMass
*/
public double getFragmentMass(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry().getMass();
}
/*public MZSeries getFragmentMZs(int ind) {
return elementAt(ind).getAnnotation().getFragmentEntry().getMZs();
}
*/
/**
Return the predicted mass/charge value associated with the peak
annotation at the specified position
@see PeakAnnotation#getAnnotationMZ
*/
public double getAnnotationMZ(int ind) {
return elementAt(ind).getAnnotationMZ();
}
/**
Return the number of charges associated with the peak
annotation at the specified position
@see PeakAnnotation#getAnnotationZ
*/
public int getAnnotationZ(int ind) {
return elementAt(ind).getAnnotationZ();
}
// serialization
/**
Create a new object from its XML representation as part of a
DOM tree.
*/
static public PeakAnnotationCollection fromXML(Node pac_node) throws Exception {
PeakAnnotationCollection ret = new PeakAnnotationCollection();
if( pac_node==null )
return ret;
Vector<Node> pa_nodes = XMLUtils.findAllChildren(pac_node, "PeakAnnotation");
for( Node pa_node : pa_nodes)
ret.addPeakAnnotation(PeakAnnotation.fromXML(pa_node));
return ret;
}
/**
Create an XML representation of this object to be part of a DOM
tree.
*/
public Element toXML(Document document) {
if( document==null )
return null;
// create root node
Element pac_node = document.createElement("PeakAnnotationCollection");
if( pac_node==null )
return null;
// add PeakAnnotation nodes
for(PeakAnnotation pa : peak_annotations) {
Element pa_node = pa.toXML(document);
if( pa_node!=null )
pac_node.appendChild(pa_node);
}
return pac_node;
}
/**
Default SAX handler to read a representation of this object
from an XML stream.
*/
public static class SAXHandler extends SAXUtils.ObjectTreeHandler {
public boolean isElement(String namespaceURI, String localName, String qName) {
return qName.equals(getNodeElementName());
}
public static String getNodeElementName() {
return "PeakAnnotationCollection";
}
protected SAXUtils.ObjectTreeHandler getHandler(String namespaceURI, String localName, String qName) {
if( qName.equals(PeakAnnotation.SAXHandler.getNodeElementName()) )
return new PeakAnnotation.SAXHandler();
return null;
}
protected Object finalizeContent(String namespaceURI, String localName, String qName) {
PeakAnnotationCollection ret = new PeakAnnotationCollection();
for( Object o : getSubObjects(PeakAnnotation.SAXHandler.getNodeElementName()) )
ret.addPeakAnnotation((PeakAnnotation)o);
return (object = ret);
}
}
public void write(TransformerHandler th) throws SAXException {
th.startElement("","","PeakAnnotationCollection",new AttributesImpl());
for(PeakAnnotation pa : peak_annotations)
pa.write(th);
th.endElement("","","PeakAnnotationCollection");
}
}