/*
* 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: 1930 $ by $Author: david@nixbioinf.org $ on $Date:: 2010-07-29 #$
*/
package org.eurocarbdb.application.glycoworkbench;
import org.eurocarbdb.application.glycanbuilder.*;
import java.util.*;
import java.io.*;
import org.jfree.data.Range;
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;
/**
* Stores a complete annotated list of peaks labeled from an MS or MS/MS
* spectrum. Each peak can have multiple annotations. The annotations can come
* from multiple structures, or from the same structure. A list of structures
* from where the annotations have been originated, as well as a list of labeled
* peaks are mantained. The annotations can be intact or fragment glycan
* molecules. In case of an MS spectrum there is only one (empty) structure from
* which the annotations are derived. The source structure can it-self be an
* intact molecule, in case of an MS/MS spectrum, or a fragment molecule, in
* case of an MSn spectrum.
*
* @author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class AnnotatedPeakList extends BaseDocument implements
SAXUtils.SAXWriter {
private double max_intensity = 0.;
private Vector<Glycan> structures;
private Vector<PeakAnnotationCollection> peak_annotations_single;
private Vector<PeakAnnotationMultiple> peak_annotations_multiple;
private Vector<String> msa_header;
// listeners
private Vector<AnnotationChangeListener> ac_listeners = new Vector<AnnotationChangeListener>();
/**
* Interface that must be implemented by all objects that want to register
* to listen for changes in the annotations
*/
public interface AnnotationChangeListener {
/**
* Called when the number or identity of the source structures are
* changed
*/
public void structuresChanged(AnnotationChangeEvent e);
/**
* Called when the number or identity of the annotations are changed
*/
public void annotationsChanged(AnnotationChangeEvent e);
}
/**
* Contains information about the event resulting from a change in the
* annotations
*/
static public class AnnotationChangeEvent {
protected AnnotatedPeakList source;
/**
* Constructor.
*
* @param _source
* the object that originated the event
*/
public AnnotationChangeEvent(AnnotatedPeakList _source) {
source = _source;
}
/**
* Return the object that originated the event
*/
public AnnotatedPeakList getSource() {
return source;
}
}
/**
* Default constructor
*/
public AnnotatedPeakList() {
super(false);
}
// -------- BaseDocument methods
public String getName() {
return "Annotated PeakList";
}
public javax.swing.ImageIcon getIcon() {
return FileUtils.defaultThemeManager.getImageIcon("annpeaksdoc");
}
public Collection<javax.swing.filechooser.FileFilter> getFileFormats() {
Vector<javax.swing.filechooser.FileFilter> filters = new Vector<javax.swing.filechooser.FileFilter>();
filters.add(new ExtensionFileFilter("msa",
"Cartoonist annotated peaklist file"));
filters.add(new ExtensionFileFilter("gwa",
"GlycoWorkbench annotated peaklist file"));
filters.add(new ExtensionFileFilter(new String[] { "gwa", "msa" },
"All annotated peaklist files"));
return filters;
}
public javax.swing.filechooser.FileFilter getAllFileFormats() {
return new ExtensionFileFilter(new String[] { "gwa", "msa" },
"Annotated peaklist files");
}
public void initData() {
max_intensity = 0.;
structures = new Vector<Glycan>();
peak_annotations_single = new Vector<PeakAnnotationCollection>();
peak_annotations_multiple = new Vector<PeakAnnotationMultiple>();
msa_header = new Vector<String>();
}
// ---- Data access
private void addStructure(Glycan structure) {
// update structures
structures.add(structure);
// update peak annotation singles
peak_annotations_single.add(new PeakAnnotationCollection());
// update peak annotation multiple
for (PeakAnnotationMultiple pam : peak_annotations_multiple)
pam.addStructure();
}
private void insertStructureAt(Glycan structure, int s_ind) {
// update structures
structures.insertElementAt(structure, s_ind);
// update peak annotation singles
peak_annotations_single.insertElementAt(new PeakAnnotationCollection(),
s_ind);
// update peak annotation multiple
for (PeakAnnotationMultiple pam : peak_annotations_multiple)
pam.insertStructureAt(s_ind);
}
private void removeStructureAt(int s_ind) {
// update structures
structures.removeElementAt(s_ind);
// update peak annotation singles
peak_annotations_single.removeElementAt(s_ind);
// update peak annotation multiple
for (PeakAnnotationMultiple pam : peak_annotations_multiple)
pam.removeStructureAt(s_ind);
if (peak_annotations_single.size() == 0)
peak_annotations_multiple.clear();
}
/**
* Return the index of the specified structure in the list of structures
* from which the annotations are originated, or -1 if the structure cannot
* be found
*/
public int indexOf(Glycan structure) {
if (structure == null)
return -1;
int ind = 0;
for (Glycan s : structures) {
if (s.compareTo(structure) == 0)
return ind;
ind++;
}
return -1;
}
/**
* Return the index of the peak object in the list of labeled peaks that
* have been annotated
*
* @see PeakAnnotationMultiple
*/
public int indexOf(Peak p) {
if (p == null)
return -1;
for (int i = 0; i < peak_annotations_multiple.size(); i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.getPeak().compareTo(p) > 0)
return -1;
if (pam.getPeak().mzEquals(p))
return i;
}
return -1;
}
/**
* Return the index of the peak object in the list of labeled peaks that
* have been annotated
*
* @see PeakAnnotationMultiple
*/
public int indexOf(Peak p, double accuracy, MassUnit unit) {
if (p == null)
return -1;
for (int i = 0; i < peak_annotations_multiple.size(); i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.getPeak().compareTo(p) > 0)
return -1;
if (pam.getPeak().mzEquals(p,unit,accuracy))
return i;
}
return -1;
}
/**
* Return the index of peak with the specified mass/charge value in the list
* of labeled peaks that have been annotated
*
* @see PeakAnnotationMultiple
*/
public int indexOf(double mz_ratio) {
double mz_tol = 0.000001;
for (int i = 0; i < peak_annotations_multiple.size(); i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.getPeak().getMZ() > mz_ratio + mz_tol)
return -1;
if (pam.getPeak().getMZ() > mz_ratio - mz_tol)
return i;
}
return -1;
}
private void addPeakAnnotationsPVT(int ind, PeakAnnotationCollection pac) {
// add the annotations to single
peak_annotations_single.elementAt(ind).addPeakAnnotations(pac);
// add the annotations to multiple
for (PeakAnnotation pa : pac.getPeakAnnotations()) {
PeakAnnotationMultiple pam = getAnnotations(pa.getPeak(), true);
pam.addAnnotation(ind, pa.getAnnotation());
}
}
private void addPeakAnnotationsPVT(Glycan structure,
PeakAnnotationCollection pac, boolean merge) {
// set structure
int ind = -1;
if (!merge || (ind = indexOf(structure)) == -1) {
addStructure(structure);
ind = structures.size() - 1;
}
addPeakAnnotationsPVT(ind, pac);
}
private void addPeakAnnotationPVT(int ind, PeakAnnotation pa) {
// add the annotations to single
peak_annotations_single.elementAt(ind).addPeakAnnotation(pa);
// add the annotations to multiple
PeakAnnotationMultiple pam = getAnnotations(pa.getPeak(), true);
pam.addAnnotation(ind, pa.getAnnotation());
}
private void addPeakAnnotationPVT(Glycan structure, PeakAnnotation pa,
boolean merge) {
// set structure
int ind = -1;
if (!merge || (ind = indexOf(structure)) == -1) {
addStructure(structure);
ind = structures.size() - 1;
}
addPeakAnnotationPVT(ind, pa);
}
/**
* Add a collection of annotations deriving from a specified structure. If
* the structure is not existing it is first added to the list
*
* @param structure
* the molecule from which the annotations have been derived
* @param pac
* the annotations collection
* @param merge
* <code>true</code> if the annotations should be merged with the
* existing ones
* @return <code>true</code> if the operation was successfull
*/
public boolean addPeakAnnotations(Glycan structure,
PeakAnnotationCollection pac, boolean merge) {
if (structure == null || pac == null)
return false;
addPeakAnnotationsPVT(structure, pac, merge);
updateIntensities();
fireStructuresChanged();
fireDocumentChanged();
return true;
}
/**
* Add a single annotation deriving from a specified structure. If the
* structure is not existing it is first added to the list
*
* @param structure
* the molecule from which the annotations have been derived
* @param pa
* the annotation
* @param merge
* <code>true</code> if the annotation should be merged with the
* existing ones
* @return <code>true</code> if the operation was successfull
*/
public boolean addPeakAnnotation(Glycan structure, PeakAnnotation pa,
boolean merge) {
if (structure == null || pa == null)
return false;
addPeakAnnotationPVT(structure, pa, merge);
updateIntensities();
fireStructuresChanged();
fireDocumentChanged();
return true;
}
/**
* Add a collection of annotations deriving from a specified structure. The
* originating structure is added at the specified position in the list
*
* @param structure
* the molecule from which the annotations have been derived
* @param pac
* the annotations collection
* @param s_ind
* the index of the structure in the list
* @return <code>true</code> if the operation was successfull
*/
public boolean insertPeakAnnotationsAt(Glycan structure,
PeakAnnotationCollection pac, int s_ind) {
if (structure == null || pac == null)
return false;
// add the structure
insertStructureAt(structure, s_ind);
// add the annotations to single
peak_annotations_single.setElementAt(pac, s_ind);
// add the annotations to multiple
for (PeakAnnotation pa : pac.getPeakAnnotations()) {
PeakAnnotationMultiple pam = getAnnotations(pa.getPeak(), true);
pam.addAnnotation(s_ind, pa.getAnnotation());
}
updateIntensities();
fireStructuresChanged();
fireDocumentChanged();
return true;
}
/**
* Remove a peak from the list of labeled peaks and clear all its
* annotations
*
* @return <code>true</code> if the operation was successfull
*/
public boolean removePeak(Peak p) {
if (p == null)
return false;
if (!removeAnnotations(p))
return false;
for (PeakAnnotationCollection pac : peak_annotations_single)
pac.removeAllPeakAnnotations(p);
updateIntensities();
fireDocumentChanged();
fireAnnotationsChanged();
return true;
}
private int removePeakAnnotation(int s_ind, PeakAnnotation pa, boolean fire) {
if (pa == null || pa.getAnnotation() == null)
return -1;
// check if the peak annotation multiple exists
PeakAnnotationMultiple pam = getAnnotations(pa.getPeak(), false);
if (pam == null)
return -1;
// check if the the peak annotation can be removed
int ret = peak_annotations_single.elementAt(s_ind)
.removePeakAnnotation(pa, true);
if (ret == -1)
return -1;
// remove the peak annotation multiple
pam.removeAnnotation(s_ind, pa.getAnnotation());
// fire events
updateIntensities();
if (fire) {
fireDocumentChanged();
fireAnnotationsChanged();
}
return ret;
}
/**
* Remove a specific annotation
*
* @param s_ind
* the index of the originating structure
* @param pa
* the annotation to remove
* @return the index of the removed annotation or <code>-1</code> if it
* couldn't be found
*/
public int removePeakAnnotation(int s_ind, PeakAnnotation pa) {
return removePeakAnnotation(s_ind, pa, true);
}
/**
* Remove a collection of annotations
*
* @param s_ind
* the index of the originating structure
* @param toremove
* the annotations to remove
* @return <code>true</code> if the operation was successfull
*/
public boolean removePeakAnnotations(int s_ind,
Collection<PeakAnnotation> toremove) {
if (toremove == null)
return false;
boolean removed = false;
for (PeakAnnotation pa : toremove)
removed |= (removePeakAnnotation(s_ind, pa, false) != -1);
updateIntensities();
if (removed) {
fireDocumentChanged();
fireAnnotationsChanged();
}
return removed;
}
/**
* Remove all annotations for a specific structure
*
* @param s_ind
* the index of the originating structure
*/
public void removePeakAnnotationsAt(int s_ind) {
removeStructureAt(s_ind);
updateIntensities();
fireStructuresChanged();
fireDocumentChanged();
}
/**
* Remove all annotations for a set of structures
*
* @param s_inds
* the index of the originating structure
*/
public void removePeakAnnotationsAt(int[] s_inds) {
Arrays.sort(s_inds);
for (int i = 0; i < s_inds.length; i++)
removeStructureAt(s_inds[i] - i);
updateIntensities();
fireStructuresChanged();
fireDocumentChanged();
}
/**
* Clear all annotations for a specified peak. Leave the peak in the list
*/
public void clearAnnotationsFor(Peak p) {
for (PeakAnnotationCollection pac : peak_annotations_single)
pac.clearAnnotations(p);
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (p.equals(pam.getPeak()))
pam.clearAnnotations();
}
updateIntensities();
fireDocumentChanged();
fireAnnotationsChanged();
}
/**
* Clear all annotations for set of peaks. Leave the peaks in the list
*/
public void clearAnnotationsFor(Collection<Peak> peaks) {
for (PeakAnnotationCollection pac : peak_annotations_single)
pac.clearAnnotations(peaks);
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (peaks.contains(pam.getPeak()))
pam.clearAnnotations();
}
updateIntensities();
fireDocumentChanged();
fireAnnotationsChanged();
}
private boolean removeAnnotations(Peak p) {
int ind = indexOf(p);
if (ind == -1)
return false;
peak_annotations_multiple.removeElementAt(ind);
return true;
}
/**
* Update the annotations for all the peaks in a range of mass/charge
* values.
*
* @param s_ind
* the index of the structure
* @param start_mz
* the minimum mass/charge value of the peaks to which the
* annotations correspond
* @param end_mz
* the maximum mass/charge value of the peaks to which the
* annotations correspond
* @param toupdate
* the new annotations
* @param merge
* <code>true</code> if the old annotations should be kept
*/
public boolean updateAnnotations(int s_ind, double start_mz, double end_mz,
Collection<PeakAnnotation> toupdate, boolean merge) {
if (toupdate == null)
return false;
PeakAnnotationCollection pac = peak_annotations_single.get(s_ind);
// remove non existing annotations
boolean removed = false;
if (!merge) {
Vector<PeakAnnotation> peak_annotations = new Vector<PeakAnnotation>(
pac.getPeakAnnotations()); // avoid concurrent modifications
for (PeakAnnotation pa : peak_annotations) {
if (start_mz == end_mz || pa.getPeak().getMZ() >= start_mz
&& pa.getPeak().getMZ() <= end_mz) {
if (!toupdate.contains(pa)) {
removed = true;
removePeakAnnotation(s_ind, pa, false);
}
}
}
}
// add new annotations
boolean added = false;
for (PeakAnnotation pa : toupdate) {
if (!pac.contains(pa)) {
added = true;
addPeakAnnotationPVT(s_ind, pa);
}
}
updateAllIntensities();
boolean changed = (removed || added);
if (changed)
fireDocumentChanged();
return changed;
}
private PeakAnnotationMultiple getAnnotations(Peak p, boolean assertive) {
// update max intensity
// max_intensity = Math.max(max_intensity, p.getIntensity());
// search position
for (int i = 0; i < peak_annotations_multiple.size(); i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.getPeak().compareTo(p) > 0) {
if (assertive) {
// insert here
PeakAnnotationMultiple toadd = new PeakAnnotationMultiple(
p, structures.size());
peak_annotations_multiple.insertElementAt(toadd, i);
return toadd;
}
return null;
}
if (pam.getPeak().equals(p))
return pam;
}
if (assertive) {
// append
PeakAnnotationMultiple toadd = new PeakAnnotationMultiple(p,
structures.size());
peak_annotations_multiple.add(toadd);
return toadd;
}
return null;
}
/**
* Return a new annotated peak list containing only the annotations with the
* best coverage
*
* @param max_num
* the number of annotations to return
* @see #getCoverage
*/
public AnnotatedPeakList getFirst(int max_num) {
// get coverages and order them
Vector<Pair<Double, Integer>> coverages = new Vector<Pair<Double, Integer>>();
for (int i = 0; i < structures.size(); i++) {
double cov = getCoverage(i);
// find right position
int ind = coverages.size();
for (int l = 0; l < coverages.size(); l++) {
if (cov > coverages.elementAt(l).getFirst()) {
ind = l;
break;
}
}
// insert new value
coverages.insertElementAt(new Pair<Double, Integer>(cov, i), ind);
}
// get best N structures
AnnotatedPeakList ret = new AnnotatedPeakList();
for (int i = 0; i < max_num && i < coverages.size(); i++) {
int ind = coverages.elementAt(i).getSecond();
ret.addPeakAnnotationsPVT(structures.elementAt(ind),
peak_annotations_single.elementAt(ind), false);
}
ret.updateIntensities();
return ret;
}
/**
* Return a new annotated peak list containing only the annotations
* originating from the specified structures
*
* @param s_indexes
* the indexes of the structures
*/
public AnnotatedPeakList extractCollections(int[] s_indexes) {
AnnotatedPeakList ret = new AnnotatedPeakList();
for (int i = 0; i < s_indexes.length; i++) {
Glycan structure = structures.elementAt(s_indexes[i]);
PeakAnnotationCollection pac = peak_annotations_single
.elementAt(s_indexes[i]);
ret.addPeakAnnotationsPVT(structure, pac, false);
}
ret.updateIntensities();
return ret;
}
/**
* Return a new annotated peak list containing only the specified
* annotations originating from a single structure
*
* @param s_ind
* the index of the structure
* @param pac_indexes
* the indexes of the annotations
* @see PeakAnnotationCollection
*/
public AnnotatedPeakList extractAnnotations(int s_ind, int[] pac_indexes) {
AnnotatedPeakList ret = new AnnotatedPeakList();
Glycan structure = structures.elementAt(s_ind);
PeakAnnotationCollection pac = peak_annotations_single.elementAt(s_ind);
ret.addStructure(structure);
for (int l = 0; l < pac_indexes.length; l++)
ret.addPeakAnnotationPVT(0, pac.getPeakAnnotation(pac_indexes[l]));
ret.updateIntensities();
return ret;
}
/**
* Return a new annotated peak list containing only the annotations for the
* specified peaks
*
* @param pam_indexes
* the indexes of the peaks
* @see PeakAnnotationMultiple
*/
public AnnotatedPeakList extractAnnotations(int[] pam_indexes) {
AnnotatedPeakList ret = new AnnotatedPeakList();
for (int i = 0; i < structures.size(); i++) {
Glycan structure = structures.elementAt(i);
PeakAnnotationCollection pac = peak_annotations_single.elementAt(i);
ret.addStructure(structure);
for (int l = 0; l < pam_indexes.length; l++) {
PeakAnnotationMultiple pam = peak_annotations_multiple
.elementAt(pam_indexes[l]);
for (PeakAnnotation pa : pac.getPeakAnnotations(pam.getPeak()))
ret.addPeakAnnotationPVT(i, pa);
}
}
ret.updateIntensities();
return ret;
}
/**
* Copy the content of another annotated peak list on the current one
*/
public void copy(AnnotatedPeakList src) {
copy(src, true);
}
/**
* Copy the content of another annotated peak list on the current one
*/
public void copy(AnnotatedPeakList src,boolean fire) {
setData(src, fire);
}
private void setData(AnnotatedPeakList src, boolean fire) {
if (src != null) {
// clear
initData();
// copy peak annotations
for (int i = 0; i < src.structures.size(); i++)
addPeakAnnotationsPVT(src.structures.elementAt(i),
src.peak_annotations_single.elementAt(i), false);
// fire events
updateIntensities();
if (fire) {
fireStructuresChanged();
fireDocumentChanged();
}
}
}
/**
* Merge the content of another annotated peak list with the current one
*/
public void merge(AnnotatedPeakList src) {
mergeData(src, true);
}
private void mergeData(AnnotatedPeakList src, boolean fire) {
if (src != null) {
// copy peak annotations
for (int i = 0; i < src.structures.size(); i++)
addPeakAnnotationsPVT(src.structures.elementAt(i),
src.peak_annotations_single.elementAt(i), true);
// fire events
updateIntensities();
if (fire) {
fireStructuresChanged();
fireDocumentChanged();
}
}
}
private void updateIntensities() {
// update intensities
max_intensity = 0.;
for (PeakAnnotationMultiple pam : peak_annotations_multiple)
max_intensity = Math.max(max_intensity, pam.getPeak()
.getIntensity());
}
private void updateAllIntensities() {
updateIntensities();
for (PeakAnnotationCollection pac : peak_annotations_single)
pac.updateIntensities();
}
/**
* Return the list of labeled peaks
*/
public Vector<Peak> getPeaks() {
Vector<Peak> ret = new Vector<Peak>();
for (PeakAnnotationMultiple pam : peak_annotations_multiple)
ret.add(pam.getPeak());
return ret;
}
/**
* Return the list of labeled peaks as a 2xN table of mass/charge and
* intensity values
*/
public double[][] getPeakData() {
int no_peaks = peak_annotations_multiple.size();
double[][] ret = new double[2][];
ret[0] = new double[no_peaks];
ret[1] = new double[no_peaks];
for (int i = 0; i < no_peaks; i++) {
Peak p = peak_annotations_multiple.elementAt(i).getPeak();
ret[0][i] = p.getMZ();
ret[1][i] = p.getIntensity();
}
return ret;
}
// members access
/**
* Return the number of labeled peaks
*
* @see #getNoPeaks
*/
public int size() {
return getNoPeaks();
}
/**
* Return the number of structures
*/
public int getNoStructures() {
return structures.size();
}
/**
* Return a specific structure
*
* @param s_ind
* the index of the structure
*/
public Glycan getStructure(int s_ind) {
return structures.elementAt(s_ind);
}
/**
* Return the list of structures
*/
public Vector<Glycan> getStructures() {
return structures;
}
/**
* Return the number of peaks
*/
public int getNoPeaks() {
return peak_annotations_multiple.size();
}
/**
* Return a specific peak
*
* @param p_ind
* the index of the peak
*/
public Peak getPeak(int p_ind) {
return peak_annotations_multiple.elementAt(p_ind).getPeak();
}
/**
* Return the mass/charge value of a specific peak
*
* @param p_ind
* the index of the peak
*/
public double getMZ(int p_ind) {
return getPeak(p_ind).getMZ();
}
/**
* Return the intensity of a specific peak
*
* @param p_ind
* the index of the peak
*/
public double getIntensity(int p_ind) {
return getPeak(p_ind).getIntensity();
}
/**
* Return the intensity of a specific peak normalize by the maximum
* intensity in the list of labeled peaks
*
* @param p_ind
* the index of the peak
*/
public double getRelativeIntensity(int p_ind) {
if (max_intensity == 0.)
return getPeak(p_ind).getIntensity();
else
return 100. * getPeak(p_ind).getIntensity() / max_intensity;
}
/**
* Return the index of the peak whose mass/charge values is nearest to the
* specified value, or -1 if the annotated peak list is empty
*/
public int nearestTo(double mz) {
if (structures.size() == 0)
return -1;
int ind = 0;
double last_err = Double.POSITIVE_INFINITY;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
double err = Math.abs(mz - pam.getPeak().getMZ());
if (err > last_err)
return ind - 1;
last_err = err;
ind++;
}
return ind - 1;
}
/**
* Return <code>true</code> if the specified peak is annotated
*/
public boolean isAnnotated(Peak p) {
return peak_annotations_multiple.elementAt(indexOf(p)).isAnnotated();
}
/**
* Return <code>true</code> if the peak at the specified position is
* annotated
*/
public boolean isAnnotated(int p_ind) {
return peak_annotations_multiple.elementAt(p_ind).isAnnotated();
}
/**
* Return all annotations for all peaks and from all structures. Each
* PeakAnnotationMultiple correspond to one peak
*/
public Collection<PeakAnnotationMultiple> getAnnotations() {
return peak_annotations_multiple;
}
public PeakAnnotationMultiple getAnnotations(Peak p) {
return getAnnotations(p, 0.0001 ,MassUnit.PPM);
}
/**
* Return all annotations for a specified peak and from all structures
*/
public PeakAnnotationMultiple getAnnotations(Peak p, double mz_accuracy, MassUnit unit) {
int comparison = indexOf(p,mz_accuracy,unit);
if (comparison != -1) {
return peak_annotations_multiple.elementAt(comparison);
} else {
return null;
}
}
/**
* Return all annotations for a specified peak and from all structures
*/
public PeakAnnotationMultiple getAnnotations(int p_ind) {
return peak_annotations_multiple.elementAt(p_ind);
}
/**
* Return all annotations for a specified peak and from a specific structure
*/
public Vector<Annotation> getAnnotations(Peak p, int s_ind) {
return peak_annotations_multiple.elementAt(indexOf(p)).getAnnotations(
s_ind);
}
/**
* Return all annotations for a specified peak and from a specific structure
*/
public Vector<Annotation> getAnnotations(int p_ind, int s_ind) {
return peak_annotations_multiple.elementAt(p_ind).getAnnotations(s_ind);
}
/**
* Return the structure of all annotations for a specified peak and from a
* specific structure
*/
public Vector<Glycan> getFragments(int p_ind, int s_ind) {
Vector<Glycan> ret = new Vector<Glycan>();
for (Annotation a : peak_annotations_multiple.elementAt(p_ind)
.getAnnotations(s_ind))
ret.add(a.getFragmentEntry().fragment);
return ret;
}
/**
* Return all annotations for all peaks and from all structures Each
* {@link PeakAnnotationCollection} correspond to one structure
*/
public Collection<PeakAnnotationCollection> getPeakAnnotationCollections() {
return peak_annotations_single;
}
/**
* Return all annotations for all peaks and from a specific structure.
*/
public PeakAnnotationCollection getPeakAnnotationCollection(int s_ind) {
return peak_annotations_single.elementAt(s_ind);
}
/**
* Return all annotations for all peaks and from a specific structure.
* Return <code>null</code> if the structure is not found
*/
public PeakAnnotationCollection getPeakAnnotationCollection(Glycan structure) {
int s_ind = structures.indexOf(structure);
if (s_ind == -1)
return null;
return peak_annotations_single.elementAt(s_ind);
}
// statistics
/**
* Return the sum of the intensities of the annotated peaks as a percentage
* of the total intensity
*
* @param s_ind
* the index of the structure
*/
public double getCoverage(int s_ind) {
double assigned = 0.;
double total = 0.;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
Peak p = pam.getPeak();
if (pam.isAnnotated(s_ind))
assigned += p.getIntensity();
total += p.getIntensity();
}
return 100. * assigned / total;
}
/**
* Return the RMSD between the mass/charge values of the peaks and the
* mass/charge values of their annotations
*
* @param s_ind
* the index of the structure
*/
public double getRMSD(int s_ind) {
int count = 0;
double rmsd = 0;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (pam.isAnnotated(s_ind)) {
rmsd += Math.pow(pam.getBestAccuracy(s_ind), 2.);
count++;
}
}
return (count > 0) ? Math.sqrt(rmsd / count) : 0.;
}
/**
* Return the RMSD between the mass/charge values of the peaks and the
* mass/charge values of their annotations in PPM
*
* @param s_ind
* the index of the structure
*/
public double getRMSD_PPM(int s_ind) {
int count = 0;
double rmsd = 0;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (pam.isAnnotated(s_ind)) {
rmsd += Math.pow(pam.getBestAccuracyPPM(s_ind), 2.);
count++;
}
}
return (count > 0) ? Math.sqrt(rmsd / count) : 0.;
}
/**
* Return the number of peaks with intensity greater than a certain value
*
* @param min_rel_int
* the minimum value of intensity
*/
public int getNoPeaks(double min_rel_int) {
int count = 0;
double min_int = min_rel_int / 100. * max_intensity;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
Peak p = pam.getPeak();
if (p.getIntensity() >= min_int)
count++;
}
return count;
}
/**
* Return the number of peaks with annotations derived from a specific
* structure
*
* @param s_ind
* the index of the structure
*/
public int getNoAnnotatedPeaks(int s_ind) {
return getNoAnnotatedPeaks(s_ind, 0.);
}
/**
* Return the number of peaks with annotations derived from a specific
* structure and intensity greater than a certain value
*
* @param s_ind
* the index of the structure
* @param min_rel_int
* the minimum value of intensity
*/
public int getNoAnnotatedPeaks(int s_ind, double min_rel_int) {
int count = 0;
double min_int = min_rel_int / 100. * max_intensity;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
Peak p = pam.getPeak();
if (p.getIntensity() >= min_int && pam.isAnnotated(s_ind))
count++;
}
return count;
}
/**
* Return the range of mass/charge values of the labeled peaks
*/
public Range getMZRange() {
return new Range(getMinMZ(), getMaxMZ());
}
/**
* Return the minimum mass/charge value of the labeled peaks
*/
public double getMinMZ() {
return peak_annotations_multiple.firstElement().getPeak().getMZ();
}
/**
* Return the maximum mass/charge value of the labeled peaks
*/
public double getMaxMZ() {
return peak_annotations_multiple.lastElement().getPeak().getMZ();
}
/**
* Return the range of accuracies for all the annotations from the specific
* structure
*/
public Range getAccuracyRange(int s_ind) {
return new Range(getMinAccuracy(s_ind), getMaxAccuracy(s_ind));
}
/**
* Return the minimum accuracy for all the annotations from the specific
* structure
*/
public double getMinAccuracy(int s_ind) {
return peak_annotations_single.elementAt(s_ind).getMinAccuracy();
}
/**
* Return the maximum accuracy for all the annotations from the specific
* structure
*/
public double getMaxAccuracy(int s_ind) {
return peak_annotations_single.elementAt(s_ind).getMaxAccuracy();
}
/**
* Return the range of accuracies in PPM for all the annotations from the
* specific structure
*/
public Range getAccuracyRangePPM(int s_ind) {
return new Range(getMinAccuracyPPM(s_ind), getMaxAccuracyPPM(s_ind));
}
/**
* Return the minimum accuracy in PPM for all the annotations from the
* specific structure
*/
public double getMinAccuracyPPM(int s_ind) {
return peak_annotations_single.elementAt(s_ind).getMinAccuracyPPM();
}
/**
* Return the maximum accuracy in PPM for all the annotations from the
* specific structure
*/
public double getMaxAccuracyPPM(int s_ind) {
return peak_annotations_single.elementAt(s_ind).getMaxAccuracyPPM();
}
/**
* Return a 2xN table of mass/charge and accuracy values for all annotated
* peaks and a specific structures. These values can be use to check the
* calibration of the spectra
*/
public double[][] getCalibrationData(int s_ind) {
PeakAnnotationCollection pac = peak_annotations_single.elementAt(s_ind);
int n = pac.size();
// count Annotated Peaks
int count = 0;
for (int i = 0; i < n; i++) {
if (pac.isAnnotated(i))
count++;
}
// get calibration data
double[][] ret = new double[2][];
ret[0] = new double[count];
ret[1] = new double[count];
for (int i = 0, l = 0; i < n; i++) {
PeakAnnotation pa = pac.elementAt(i);
if (pa.isAnnotated()) {
ret[0][l] = pa.getPeak().getMZ();
ret[1][l] = pa.getAccuracy();
l++;
}
}
return ret;
}
/**
* Return a 2xN table of mass/charge and accuracy values for all annotated
* peaks and a specific structures. Only the most accurate annotation for
* each peak is used. These values can be use to check the calibration of
* the spectra
*/
public double[][] getBestCalibrationData(int s_ind) {
// count Annotated Peaks
int count = 0;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (pam.isAnnotated(s_ind))
count++;
}
// get calibration data
int n = peak_annotations_multiple.size();
double[][] ret = new double[2][];
ret[0] = new double[count];
ret[1] = new double[count];
for (int i = 0, l = 0; i < n; i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.isAnnotated(s_ind)) {
ret[0][l] = pam.getPeak().getMZ();
ret[1][l] = pam.getBestAccuracy(s_ind);
l++;
}
}
return ret;
}
/**
* Return a 2xN table of mass/charge and accuracy values in PPM for all
* annotated peaks and a specific structures. These values can be use to
* check the calibration of the spectra
*/
public double[][] getCalibrationDataPPM(int s_ind) {
PeakAnnotationCollection pac = peak_annotations_single.elementAt(s_ind);
int n = size();
// count Annotated Peaks
int count = 0;
for (int i = 0; i < n; i++) {
if (pac.isAnnotated(i))
count++;
}
// get calibration data
double[][] ret = new double[2][];
ret[0] = new double[count];
ret[1] = new double[count];
for (int i = 0, l = 0; i < n; i++) {
PeakAnnotation pa = pac.elementAt(i);
if (pa.isAnnotated()) {
ret[0][l] = pa.getPeak().getMZ();
ret[1][l] = pa.getAccuracyPPM();
l++;
}
}
return ret;
}
/**
* Return a 2xN table of mass/charge and accuracy values in PPM for all
* annotated peaks and a specific structures. Only the most accurate
* annotation for each peak is used. These values can be use to check the
* calibration of the spectra
*/
public double[][] getBestCalibrationDataPPM(int s_ind) {
// count Annotated Peaks
int count = 0;
for (PeakAnnotationMultiple pam : peak_annotations_multiple) {
if (pam.isAnnotated(s_ind))
count++;
}
// get calibration data
int n = peak_annotations_multiple.size();
double[][] ret = new double[2][];
ret[0] = new double[count];
ret[1] = new double[count];
for (int i = 0, l = 0; i < n; i++) {
PeakAnnotationMultiple pam = peak_annotations_multiple.elementAt(i);
if (pam.isAnnotated(s_ind)) {
ret[0][l] = pam.getPeak().getMZ();
ret[1][l] = pam.getBestAccuracyPPM(s_ind);
l++;
}
}
return ret;
}
private boolean updatePeakPVT(Peak p) {
int ind = indexOf(p.getMZ());
if (ind == -1)
return false;
PeakAnnotationMultiple pam = peak_annotations_multiple.get(ind);
pam.getPeak().setIntensity(p.getIntensity());
for (PeakAnnotationCollection pac : peak_annotations_single) {
for (PeakAnnotation pa : pac.getPeakAnnotations(p.getMZ()))
pa.getPeak().setIntensity(p.getIntensity());
}
return true;
}
/**
* Notify the document of a change in the intensity for a specific peak
*/
public boolean updatePeak(Peak p) {
if (updatePeakPVT(p)) {
updateAllIntensities();
fireDocumentChanged();
return true;
}
return false;
}
/**
* Notify the document of a change in the intensity for a list of peaks
*/
public boolean updatePeaks(Collection<Peak> peaks) {
boolean changed = false;
for (Peak p : peaks)
changed |= updatePeakPVT(p);
if (changed) {
updateAllIntensities();
fireDocumentChanged();
}
return changed;
}
// --------------
// events
/**
* Register a listener to receive events when the annotations are changed
*/
public void addAnnotationChangeListener(AnnotationChangeListener l) {
if (l != null)
ac_listeners.add(l);
}
/**
* De-register one of the listeners that was receiving events when the
* annotations are changed
*/
public void removeAnnotationChangeListener(AnnotationChangeListener l) {
if (l != null)
ac_listeners.remove(l);
}
/**
* Notify all listeners of a change in the number or identity of the
* structures
*/
public void fireStructuresChanged() {
if (ac_listeners != null) {
for (AnnotationChangeListener acl : ac_listeners)
acl.structuresChanged(new AnnotationChangeEvent(this));
}
}
/**
* Notify all listeners of a change in the number or identity of the
* annotations
*/
public void fireAnnotationsChanged() {
if (ac_listeners != null) {
for (AnnotationChangeListener acl : ac_listeners)
acl.annotationsChanged(new AnnotationChangeEvent(this));
}
}
public void fireDocumentInit() {
super.fireDocumentInit();
fireStructuresChanged();
}
public void fireDocumentInit(BaseDocument source) {
super.fireDocumentInit(source);
if (source == this)
fireStructuresChanged();
}
// ----------------------------------------
// Serialization
public void fromString(String str, boolean merge) throws Exception {
fromString(str, merge, false);
}
public void fromString(String str, boolean merge, boolean fire)
throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
read(bis, merge);
if (fire) {
fireStructuresChanged();
fireDocumentChanged();
}
}
protected void read(InputStream is, boolean merge) throws Exception {
BufferedInputStream bis = new BufferedInputStream(is);
if (bis.markSupported()) {
// check for MSA content
bis.mark(100);
byte[] buf = new byte[18];
if (bis.read(buf, 0, 18) == 18) {
bis.reset();
String str_buf = new String(buf);
if (str_buf.startsWith("# .msa version 002")) {
this.readMSA2(bis, merge);
return;
} else if (str_buf.startsWith("# .msa version 003")) {
this.readMSA3(bis, merge);
return;
}
} else
bis.reset();
}
SAXUtils.read(bis, new SAXHandler(this, merge));
}
public String toString() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
write(bos);
return bos.toString();
} catch (Exception e) {
LogUtils.report(e);
return "";
}
}
public void write(OutputStream os) throws Exception {
/*
* Document document = XMLUtils.newDocument(); if( document==null )
* return "";
*
* Element apl_node = toXML(document); if( apl_node == null ) return "";
*
* document.appendChild(apl_node); XMLUtils.write(bos,document);
*/
SAXUtils.write(os, this);
}
/**
* Create a new document from its XML representation as part of a DOM tree.
*/
public void fromXML(Node apl_node, boolean merge) throws Exception {
// clear
if (!merge) {
resetStatus();
initData();
} else
setChanged(true);
// read annotations
Vector<Node> ann_nodes = XMLUtils.findAllChildren(apl_node,
"Annotations");
for (Node ann_node : ann_nodes) {
Node s_node = XMLUtils.assertChild(ann_node, "Glycan");
Glycan structure = Glycan.fromXML(s_node, new MassOptions());
Node pac_node = XMLUtils.assertChild(ann_node,
"PeakAnnotationCollection");
PeakAnnotationCollection pac = PeakAnnotationCollection
.fromXML(pac_node);
addPeakAnnotationsPVT(structure, pac, true);
}
updateIntensities();
}
/**
* 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 apl_node = document.createElement("AnnotatedPeakList");
if (apl_node == null)
return null;
// add annotations
for (int i = 0; i < structures.size(); i++) {
Element s_node = structures.elementAt(i).toXML(document);
if (s_node == null)
continue;
Element pac_node = peak_annotations_single.elementAt(i).toXML(
document);
if (pac_node == null)
continue;
Element ann_node = document.createElement("Annotations");
if (ann_node == null)
continue;
ann_node.appendChild(s_node);
ann_node.appendChild(pac_node);
apl_node.appendChild(ann_node);
}
return apl_node;
}
/**
* Default SAX handler to read a representation of all annotations of a
* single structure from an XML stream.
*/
public static class AnnotationsSAXHandler extends
SAXUtils.ObjectTreeHandler {
public boolean isElement(String namespaceURI, String localName,
String qName) {
return qName.equals(getNodeElementName());
}
/**
* Return the element tag recognized by this handler
*/
public static String getNodeElementName() {
return "Annotations";
}
protected SAXUtils.ObjectTreeHandler getHandler(String namespaceURI,
String localName, String qName) {
if (qName.equals(Glycan.SAXHandler.getNodeElementName()))
return new Glycan.SAXHandler(new MassOptions());
if (qName.equals(PeakAnnotationCollection.SAXHandler
.getNodeElementName()))
return new PeakAnnotationCollection.SAXHandler();
return null;
}
protected Object finalizeContent(String namespaceURI, String localName,
String qName) throws SAXException {
Glycan g = (Glycan) getSubObject(Glycan.SAXHandler
.getNodeElementName(), true);
PeakAnnotationCollection pac = (PeakAnnotationCollection) getSubObject(
PeakAnnotationCollection.SAXHandler.getNodeElementName(),
true);
return new Pair<Glycan, PeakAnnotationCollection>(g, pac);
}
}
/**
* Default SAX handler to read a representation of this object from an XML
* stream.
*/
public static class SAXHandler extends SAXUtils.ObjectTreeHandler {
private AnnotatedPeakList theDocument;
private boolean merge;
public SAXHandler(AnnotatedPeakList _doc, boolean _merge) {
theDocument = _doc;
merge = _merge;
}
public boolean isElement(String namespaceURI, String localName,
String qName) {
return qName.equals(getNodeElementName());
}
public static String getNodeElementName() {
return "AnnotatedPeakList";
}
protected SAXUtils.ObjectTreeHandler getHandler(String namespaceURI,
String localName, String qName) {
if (qName.equals(AnnotationsSAXHandler.getNodeElementName()))
return new AnnotationsSAXHandler();
return null;
}
protected Object finalizeContent(String namespaceURI, String localName,
String qName) throws SAXException {
// clear
if (!merge) {
theDocument.resetStatus();
theDocument.initData();
} else
theDocument.setChanged(true);
// read data
for (Object o : getSubObjects(AnnotationsSAXHandler
.getNodeElementName())) {
Pair<Glycan, PeakAnnotationCollection> p = (Pair<Glycan, PeakAnnotationCollection>) o;
theDocument.addPeakAnnotationsPVT(p.getFirst(), p.getSecond(),
true);
}
theDocument.updateIntensities();
return (object = theDocument);
}
}
public void write(TransformerHandler th) throws SAXException {
th.startElement("", "", "AnnotatedPeakList", new AttributesImpl());
for (int i = 0; i < structures.size(); i++) {
th.startElement("", "", "Annotations", new AttributesImpl());
structures.elementAt(i).write(th);
peak_annotations_single.elementAt(i).write(th);
th.endElement("", "", "Annotations");
}
th.endElement("", "", "AnnotatedPeakList");
}
// integration with cartoonist
private void readMSA2(InputStream is, boolean merge) throws Exception {
if (!merge) {
this.resetStatus();
this.initData();
} else
this.setChanged(true);
String line;
Glycan empty = new Glycan();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
// parse line
if (line.length() == 0 || line.startsWith("#")) {
// store comments in the msa header
msa_header.add(line);
continue;
}
Vector<String> tokens = TextUtils.tokenize(line, " ");
if (tokens.size() < 15)
throw new Exception("Invalid format for line: " + line);
// get peak
double mz_value = Double.valueOf(tokens.get(1));
int offset = Integer.valueOf(tokens.get(2));
if (offset > 0)
mz_value -= (double) offset; // reduce to C12 isotope
Peak peak = new Peak(mz_value, 1);
if (peak == null)
throw new Exception("m/z value not found in peaklist: "
+ mz_value);
// get cartoons
Vector<Glycan> cartoons = new Vector<Glycan>();
if (tokens.size() > 15) {
// set default mass options for MALDI on permethylated glycans
MassOptions mopt = new MassOptions(MassOptions.PERMETHYLATED,
"freeEnd"); // !!! o-glycans
mopt.ION_CLOUD = new IonCloud("Na");
// parse cartoons
GlycoMindsParser parser = new GlycoMindsParser();
String str_cartoons = tokens.get(15);
str_cartoons = str_cartoons.substring(1,
str_cartoons.length() - 1);
for (String str_cartoon : TextUtils.tokenize(str_cartoons, ";"))
cartoons.add(parser.readGlycan(str_cartoon, mopt));
}
// add annotations
if (cartoons.size() > 0) {
// add cartoon annotations
for (Glycan cartoon : cartoons)
this.addPeakAnnotation(empty, new PeakAnnotation(peak,
new FragmentEntry(cartoon, "")), true);
} else {
// add empty annotation
this.addPeakAnnotation(empty, new PeakAnnotation(peak), true);
}
}
}
private void readMSA3(InputStream is, boolean merge) throws Exception {
if (!merge) {
this.resetStatus();
this.initData();
} else
this.setChanged(true);
String line;
Glycan empty = new Glycan();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
// parse line
if (line.length() == 0 || line.startsWith("#")) {
// store comments in the msa header
msa_header.add(line);
continue;
}
Vector<String> tokens = TextUtils.tokenize(line, " ");
if (tokens.size() < 17)
throw new Exception("Invalid format for line: " + line);
// get peak
double mz_value = Double.valueOf(tokens.get(1));
int offset = Integer.valueOf(tokens.get(2));
if (offset > 0)
mz_value -= (double) offset; // reduce to C12 isotope
double int_value = Double.valueOf(tokens.get(5));
Peak peak = new Peak(mz_value, int_value);
// get cartoons
Vector<Glycan> cartoons = new Vector<Glycan>();
if (tokens.size() > 17) {
// set default mass options for MALDI on permethylated glycans
MassOptions mopt = new MassOptions(MassOptions.PERMETHYLATED,
"freeEnd"); // !!! o-glycans
mopt.ION_CLOUD = parseCharges(tokens.get(3));
// parse cartoons
GlycoMindsParser parser = new GlycoMindsParser();
String str_cartoons = tokens.get(17);
str_cartoons = str_cartoons.substring(1,
str_cartoons.length() - 1);
for (String str_cartoon : TextUtils.tokenize(str_cartoons, ";"))
cartoons.add(parser.readGlycan(str_cartoon, mopt));
}
// add annotations
if (cartoons.size() > 0) {
// add cartoon annotations
for (Glycan cartoon : cartoons)
this.addPeakAnnotation(empty, new PeakAnnotation(peak,
new FragmentEntry(cartoon, "")), true);
} else {
// add empty annotation
this.addPeakAnnotation(empty, new PeakAnnotation(peak), true);
}
}
}
private IonCloud parseCharges(String charges) throws Exception {
IonCloud ret = new IonCloud();
String[] tokens = charges.split(",");
for (int i = 0; i < tokens.length; i++) {
// parse count;
int c = 0;
for (; c < tokens[i].length()
&& Character.isDigit(tokens[i].charAt(c)); c++)
;
int count = 1;
if (c > 0)
count = Integer.valueOf(tokens[i].substring(0, c));
// parse ion
String ion = tokens[i].substring(c);
if (ion.equals("H+"))
ret.add(MassOptions.ION_H, count);
else if (ion.equals("Na+"))
ret.add(MassOptions.ION_NA, count);
else if (ion.equals("Li+"))
ret.add(MassOptions.ION_LI, count);
else if (ion.equals("K+"))
ret.add(MassOptions.ION_K, count);
else
throw new Exception("Unrecognized ion " + ion);
}
return ret;
}
}