package com.compomics.util.experiment.biology; import com.compomics.util.experiment.biology.ions.ImmoniumIon; import com.compomics.util.experiment.biology.ions.PeptideFragmentIon; import com.compomics.util.experiment.biology.ions.PrecursorIon; import com.compomics.util.experiment.biology.ions.RelatedIon; import com.compomics.util.experiment.biology.ions.ReporterIon; import com.compomics.util.experiment.biology.ions.TagFragmentIon; import com.compomics.util.experiment.identification.matches.ModificationMatch; import com.compomics.util.experiment.identification.amino_acid_tags.Tag; import com.compomics.util.experiment.identification.amino_acid_tags.TagComponent; import com.compomics.util.experiment.identification.identification_parameters.PtmSettings; import com.compomics.util.experiment.identification.spectrum_annotation.SpecificAnnotationSettings; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; /** * This factory generates the expected ions from a peptide. * * @author Marc Vaudel * @author Harald Barsnes */ public class IonFactory { /** * The instance of the factory. */ private static IonFactory instance = null; /** * Neutral losses which will be looked for for every peptide independently * of the modifications found. */ private static ArrayList<NeutralLoss> defaultNeutralLosses = null; /** * Constructor. */ private IonFactory() { } /** * Static method which returns the instance of the factory. * * @return the instance of the factory */ public static IonFactory getInstance() { if (instance == null) { instance = new IonFactory(); } return instance; } /** * Returns the default neutral losses. * * @return the default neutral losses */ public static ArrayList<NeutralLoss> getDefaultNeutralLosses() { if (defaultNeutralLosses == null) { setDefaultNeutralLosses(); } return defaultNeutralLosses; } /** * Sets the default neutral losses. */ private static synchronized void setDefaultNeutralLosses() { if (defaultNeutralLosses == null) { defaultNeutralLosses = new ArrayList<NeutralLoss>(2); defaultNeutralLosses.add(NeutralLoss.H2O); defaultNeutralLosses.add(NeutralLoss.NH3); } } /** * Returns a list containing the default neutral losses and the losses found * in the given modifications. Note: modifications must be loaded in the PTM * factory. * * @param ptmSettings the PTM settings * * @return the neutral losses expected in the dataset */ public static ArrayList<NeutralLoss> getNeutralLosses(PtmSettings ptmSettings) { ArrayList<NeutralLoss> neutralLosses = new ArrayList<NeutralLoss>(); neutralLosses.addAll(IonFactory.getDefaultNeutralLosses()); PTMFactory ptmFactory = PTMFactory.getInstance(); for (String modification : ptmSettings.getAllModifications()) { PTM currentPtm = ptmFactory.getPTM(modification); boolean found = false; for (NeutralLoss ptmNeutralLoss : currentPtm.getNeutralLosses()) { for (NeutralLoss neutralLoss : neutralLosses) { if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { neutralLosses.add(ptmNeutralLoss); } } } return neutralLosses; } /** * Returns the reporter ions to annotate with the given PTM settings. * * @param ptmSettings the PTMs to annotate * * @return a hashset of the subtype indexes of the reporter ions to annotate */ public static HashSet<Integer> getReporterIons(PtmSettings ptmSettings) { HashSet<Integer> reporterIons = new HashSet<Integer>(); PTMFactory ptmFactory = PTMFactory.getInstance(); for (String modification : ptmSettings.getAllModifications()) { PTM currentPtm = ptmFactory.getPTM(modification); for (ReporterIon reporterIon : currentPtm.getReporterIons()) { reporterIons.add(reporterIon.getSubType()); } } return reporterIons; } /** * This method returns all the theoretic ions expected from a peptide. /!\ * this method will work only if the PMTs found in the peptide are in the * PTMFactory. * * @param peptide The considered peptide * * @return the expected fragment ions */ public HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> getFragmentIons(Peptide peptide) { return getFragmentIons(peptide, null); } /** * This method returns the theoretic ions expected from a peptide. /!\ this * method will work only if the PMTs found in the peptide are in the * PTMFactory. * * @param peptide The considered peptide * @param specificAnnotationSettings if provided, only the ions detectable * using these settings will be selected * * @return the expected fragment ions */ public HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> getFragmentIons(Peptide peptide, SpecificAnnotationSettings specificAnnotationSettings) { HashMap<Ion.IonType, HashSet<Integer>> selectedIonTypes = null; if (specificAnnotationSettings != null) { selectedIonTypes = specificAnnotationSettings.getIonTypes(); } HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> result = new HashMap<Integer, HashMap<Integer, ArrayList<Ion>>>(); String sequence = peptide.getSequence(); HashMap<Integer, ArrayList<PTM>> modifications = new HashMap<Integer, ArrayList<PTM>>(peptide.getNModifications()); PTMFactory ptmFactory = PTMFactory.getInstance(); ArrayList<String> processedPtms = null; ArrayList<NeutralLoss> possibleNeutralLosses = null; if (specificAnnotationSettings == null || !specificAnnotationSettings.getNeutralLossesMap().isEmpty()) { possibleNeutralLosses = new ArrayList<NeutralLoss>(getDefaultNeutralLosses()); } if (peptide.isModified()) { for (ModificationMatch ptmMatch : peptide.getModificationMatches()) { int location = ptmMatch.getModificationSite(); String ptmName = ptmMatch.getTheoreticPtm(); PTM ptm = ptmFactory.getPTM(ptmName); if (ptm == null) { throw new IllegalArgumentException("PTM " + ptmName + " not loaded in the PTM factory."); } ArrayList<PTM> modificationsAtSite = modifications.get(location); if (modificationsAtSite == null) { modificationsAtSite = new ArrayList<PTM>(1); modifications.put(location, modificationsAtSite); } modificationsAtSite.add(ptm); if (processedPtms == null || !processedPtms.contains(ptmName)) { if (selectedIonTypes == null || selectedIonTypes.keySet().contains(Ion.IonType.REPORTER_ION)) { for (ReporterIon ptmReporterIon : ptm.getReporterIons()) { HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.REPORTER_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(ptm.getReporterIons().size()); result.put(Ion.IonType.REPORTER_ION.index, ionsMap); } int subType = ptmReporterIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); ionsMap.put(subType, ions); ions.add(ptmReporterIon); } } } if (specificAnnotationSettings == null || !specificAnnotationSettings.getNeutralLossesMap().isEmpty()) { for (NeutralLoss ptmNeutralLoss : ptm.getNeutralLosses()) { boolean found = false; for (NeutralLoss neutralLoss : possibleNeutralLosses) { // @TODO: we keep only different neutral losses. We might want to change that when people // are working with modifications having reproducible motifs like ubiquitin or some glycons. if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { possibleNeutralLosses.add(ptmNeutralLoss); } } } if (processedPtms == null) { processedPtms = new ArrayList<String>(peptide.getNModifications()); } processedPtms.add(ptmName); } } } // We account for up to two neutral losses per ion maximum ArrayList<ArrayList<NeutralLoss>> neutralLossesCombinations = null; if (specificAnnotationSettings == null || !specificAnnotationSettings.getNeutralLossesMap().isEmpty()) { neutralLossesCombinations = getAccountedNeutralLosses(possibleNeutralLosses); } double forwardMass = 0; double rewindMass = Atom.O.getMonoisotopicMass(); for (int aa = 0; aa < sequence.length() - 1; aa++) { char aaName = sequence.charAt(aa); // immonium ions if (selectedIonTypes == null || selectedIonTypes.keySet().contains(Ion.IonType.IMMONIUM_ION)) { HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.IMMONIUM_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(sequence.length()); result.put(Ion.IonType.IMMONIUM_ION.index, ionsMap); } ImmoniumIon immoniumIon = new ImmoniumIon(aaName); int subType = immoniumIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); ions.add(immoniumIon); ionsMap.put(subType, ions); } } // related ions if (selectedIonTypes == null || selectedIonTypes.keySet().contains(Ion.IonType.RELATED_ION)) { HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.RELATED_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(sequence.length()); result.put(Ion.IonType.RELATED_ION.index, ionsMap); } ArrayList<RelatedIon> relatedIons = RelatedIon.getRelatedIons(AminoAcid.getAminoAcid(aaName)); for (RelatedIon tempRelated : relatedIons) { int subType = tempRelated.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); } ions.add(tempRelated); ionsMap.put(subType, ions); } } int faa = aa + 1; AminoAcid currentAA = AminoAcid.getAminoAcid(aaName); forwardMass += currentAA.getMonoisotopicMass(); if (modifications.get(faa) != null) { for (PTM ptm : modifications.get(faa)) { forwardMass += ptm.getMass(); } } HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.PEPTIDE_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(6); result.put(Ion.IonType.PEPTIDE_FRAGMENT_ION.index, ionsMap); } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.A_ION)) { // add the a-ions int subType = PeptideFragmentIon.A_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass(), null)); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.B_ION)) { // add the b-ions int subType = PeptideFragmentIon.B_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, forwardMass - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, forwardMass, null)); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.C_ION)) { // add the c-ion int subType = PeptideFragmentIon.C_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass(), null)); } } int raa = sequence.length() - aa - 1; currentAA = AminoAcid.getAminoAcid(sequence.charAt(raa)); rewindMass += currentAA.getMonoisotopicMass(); if (modifications.get(raa + 1) != null) { for (PTM ptm : modifications.get(raa + 1)) { rewindMass += ptm.getMass(); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.X_ION)) { // add the x-ion int subType = PeptideFragmentIon.X_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, rewindMass + Atom.C.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, rewindMass + Atom.C.getMonoisotopicMass() + Atom.O.getMonoisotopicMass(), null)); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.Y_ION)) { // add the y-ions int subType = PeptideFragmentIon.Y_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, rewindMass + 2 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, rewindMass + 2 * Atom.H.getMonoisotopicMass(), null)); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PEPTIDE_FRAGMENT_ION) && specificAnnotationSettings.getFragmentIonTypes().contains(PeptideFragmentIon.Z_ION)) { // add the z-ions int subType = PeptideFragmentIon.Z_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PeptideFragmentIon(subType, faa, rewindMass - Atom.N.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PeptideFragmentIon(subType, faa, rewindMass - Atom.N.getMonoisotopicMass(), null)); } } } AminoAcid currentAA = AminoAcid.getAminoAcid(sequence.charAt(sequence.length() - 1)); forwardMass += currentAA.getMonoisotopicMass(); if (modifications.get(sequence.length()) != null) { for (PTM ptm : modifications.get(sequence.length())) { forwardMass += ptm.getMass(); } } if (specificAnnotationSettings == null || selectedIonTypes.keySet().contains(Ion.IonType.PRECURSOR_ION)) { // add the precursor ion HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.PRECURSOR_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(1); result.put(Ion.IonType.PRECURSOR_ION.index, ionsMap); } int subType = PrecursorIon.PRECURSOR; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { if (neutralLossesCombinations != null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); } else { ions = new ArrayList<Ion>(1); } ionsMap.put(subType, ions); } if (neutralLossesCombinations != null) { for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PrecursorIon(forwardMass + (2 * Atom.H.getMonoisotopicMass()) + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses)); } } else { ions.add(new PrecursorIon(forwardMass + Atom.H.getMonoisotopicMass() + Atom.O.getMonoisotopicMass(), null)); } } return result; } /** * This method returns the theoretic ions expected from a tag. * * /!\ this method will work only if the PTMs found in the tag are in the * PTMFactory. * * @param tag the considered tag * @return the expected fragment ions */ public HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> getFragmentIons(Tag tag) { HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> result = new HashMap<Integer, HashMap<Integer, ArrayList<Ion>>>(); ArrayList<NeutralLoss> possibleNeutralLosses = new ArrayList<NeutralLoss>(getDefaultNeutralLosses()); ArrayList<String> processedPtms = null; // We will account for up to two neutral losses per ion maximum ArrayList<ArrayList<NeutralLoss>> neutralLossesCombinations = getAccountedNeutralLosses(possibleNeutralLosses); int ionNumberOffset = 1; ArrayList<Double> massOffsets = new ArrayList<Double>(); massOffsets.add(0.0); for (TagComponent tagComponent : tag.getContent()) { if (tagComponent instanceof AminoAcidPattern) { AminoAcidPattern aminoAcidPattern = (AminoAcidPattern) tagComponent; ArrayList<Double> patternMasses = new ArrayList<Double>(); for (int i = 0; i < aminoAcidPattern.length(); i++) { ArrayList<Double> aminoAcidMasses = new ArrayList<Double>(); for (Character aa : aminoAcidPattern.getTargetedAA(i)) { AminoAcid aminoAcid = AminoAcid.getAminoAcid(aa); double mass = aminoAcid.getMonoisotopicMass(); for (ModificationMatch modificationMatch : aminoAcidPattern.getModificationsAt(i + 1)) { String ptmName = modificationMatch.getTheoreticPtm(); PTM ptm = PTMFactory.getInstance().getPTM(ptmName); if (processedPtms == null || !processedPtms.contains(ptmName)) { for (ReporterIon ptmReporterIon : ptm.getReporterIons()) { HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.REPORTER_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.REPORTER_ION.index, ionsMap); } int subType = ptmReporterIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); ions.add(ptmReporterIon); } } for (NeutralLoss ptmNeutralLoss : ptm.getNeutralLosses()) { boolean found = false; for (NeutralLoss neutralLoss : possibleNeutralLosses) { // @TODO: we keep only different neutral losses. We might want to change that when people // are working with modifications having reproducible motifs like ubiquitin or some glycons. if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { possibleNeutralLosses.add(ptmNeutralLoss); } } if (processedPtms == null) { processedPtms = new ArrayList<String>(); } processedPtms.add(ptmName); } mass += ptm.getMass(); } if (!aminoAcidMasses.contains(mass)) { aminoAcidMasses.add(mass); } // immonium ions HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.IMMONIUM_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.IMMONIUM_ION.index, ionsMap); } ImmoniumIon immoniumIon = new ImmoniumIon(aminoAcid.getSingleLetterCodeAsChar()); int subType = immoniumIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ions.add(immoniumIon); ionsMap.put(subType, ions); } // related ions ionsMap = result.get(Ion.IonType.RELATED_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.RELATED_ION.index, ionsMap); } ArrayList<RelatedIon> relatedIons = RelatedIon.getRelatedIons(aminoAcid); for (RelatedIon tempRelated : relatedIons) { subType = tempRelated.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); } ions.add(tempRelated); ionsMap.put(subType, ions); } } HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { ArrayList<Double> newPatternMassess = new ArrayList<Double>(); if (patternMasses.isEmpty()) { for (double mass : aminoAcidMasses) { int aa = ionNumberOffset + i; int subaa = i + 1; double forwardMass = massOffset + mass; // add the a-ions int subType = TagFragmentIon.A_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } // add the b-ions subType = TagFragmentIon.B_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - getLossesMass(losses), losses, massOffset)); } // add the c-ion subType = TagFragmentIon.B_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } if (!newPatternMassess.contains(mass)) { newPatternMassess.add(mass); } } } else { for (double patternMass : patternMasses) { for (double mass : aminoAcidMasses) { int aa = ionNumberOffset + i; int subaa = i + 1; double patternFragmentMass = patternMass + mass; double forwardMass = massOffset + patternFragmentMass; // add the a-ions int subType = TagFragmentIon.A_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } // add the b-ions subType = TagFragmentIon.B_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - getLossesMass(losses), losses, massOffset)); } // add the c-ion subType = TagFragmentIon.C_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } if (!newPatternMassess.contains(patternFragmentMass)) { newPatternMassess.add(patternFragmentMass); } } } } patternMasses = newPatternMassess; } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { for (double mass : patternMasses) { double newMass = offsetMass + mass; if (!newOffsetMasses.contains(newMass)) { newOffsetMasses.add(newMass); } } } massOffsets = newOffsetMasses; ionNumberOffset += aminoAcidPattern.length(); } else if (tagComponent instanceof AminoAcidSequence) { AminoAcidSequence aminoAcidSequence = (AminoAcidSequence) tagComponent; double sequenceMass = 0; for (int i = 0; i < aminoAcidSequence.length(); i++) { AminoAcid aminoAcid = aminoAcidSequence.getAminoAcidAt(i); // immonium ions HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.IMMONIUM_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.IMMONIUM_ION.index, ionsMap); } ImmoniumIon immoniumIon = new ImmoniumIon(aminoAcid.getSingleLetterCodeAsChar()); int subType = immoniumIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ions.add(immoniumIon); ionsMap.put(subType, ions); } // related ions ionsMap = result.get(Ion.IonType.RELATED_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.RELATED_ION.index, ionsMap); } ArrayList<RelatedIon> relatedIons = RelatedIon.getRelatedIons(aminoAcid); for (RelatedIon tempRelated : relatedIons) { subType = tempRelated.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); } ions.add(tempRelated); ionsMap.put(subType, ions); } double mass = aminoAcid.getMonoisotopicMass(); for (ModificationMatch modificationMatch : aminoAcidSequence.getModificationsAt(i + 1)) { String ptmName = modificationMatch.getTheoreticPtm(); PTM ptm = PTMFactory.getInstance().getPTM(ptmName); if (processedPtms == null || !processedPtms.contains(ptmName)) { for (ReporterIon ptmReporterIon : ptm.getReporterIons()) { ionsMap = result.get(Ion.IonType.REPORTER_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.REPORTER_ION.index, ionsMap); } subType = ptmReporterIon.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); ions.add(ptmReporterIon); } } for (NeutralLoss ptmNeutralLoss : ptm.getNeutralLosses()) { boolean found = false; for (NeutralLoss neutralLoss : possibleNeutralLosses) { // @TODO: we keep only different neutral losses. We might want to change that when people // are working with modifications having reproducible motifs like ubiquitin or some glycons. if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { possibleNeutralLosses.add(ptmNeutralLoss); } } if (processedPtms == null) { processedPtms = new ArrayList<String>(); } processedPtms.add(ptmName); } mass += ptm.getMass(); } sequenceMass += mass; ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { int aa = ionNumberOffset + i; int subaa = i + 1; double forwardMass = massOffset + sequenceMass; // add the a-ions subType = TagFragmentIon.A_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } // add the b-ions subType = TagFragmentIon.B_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - getLossesMass(losses), losses, massOffset)); } // add the c-ion subType = TagFragmentIon.C_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { double newMass = offsetMass + sequenceMass; if (!newOffsetMasses.contains(newMass)) { newOffsetMasses.add(newMass); } } massOffsets = newOffsetMasses; ionNumberOffset += aminoAcidSequence.length(); } else if (tagComponent instanceof MassGap) { double gapMass = tagComponent.getMass(); int aa = ionNumberOffset; int subaa = 0; HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { double forwardMass = massOffset + gapMass; // add the a-ions int subType = TagFragmentIon.A_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - Atom.C.getMonoisotopicMass() - Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } // add the b-ions subType = TagFragmentIon.B_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass - getLossesMass(losses), losses, massOffset)); } // add the c-ion subType = TagFragmentIon.C_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, forwardMass + Atom.N.getMonoisotopicMass() + 3 * Atom.H.getMonoisotopicMass() - getLossesMass(losses), losses, massOffset)); } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { newOffsetMasses.add(offsetMass + gapMass); } massOffsets = newOffsetMasses; ionNumberOffset++; } else { throw new UnsupportedOperationException("Fragment ion not implemented for tag component " + tagComponent.getClass() + "."); } } ArrayList<TagComponent> reversedTag = new ArrayList<TagComponent>(tag.getContent()); Collections.reverse(reversedTag); ionNumberOffset = 0; massOffsets.clear(); massOffsets.add(0.0); for (TagComponent tagComponent : reversedTag) { if (tagComponent instanceof AminoAcidPattern) { AminoAcidPattern aminoAcidPattern = (AminoAcidPattern) tagComponent; ArrayList<Double> patternMasses = new ArrayList<Double>(); for (int i = aminoAcidPattern.length() - 1; i >= 0; i--) { ArrayList<Double> aminoAcidMasses = new ArrayList<Double>(); for (Character aa : aminoAcidPattern.getTargetedAA(i)) { AminoAcid aminoAcid = AminoAcid.getAminoAcid(aa); double mass = aminoAcid.getMonoisotopicMass(); for (ModificationMatch modificationMatch : aminoAcidPattern.getModificationsAt(i + 1)) { String ptmName = modificationMatch.getTheoreticPtm(); PTM ptm = PTMFactory.getInstance().getPTM(ptmName); if (processedPtms == null || !processedPtms.contains(ptmName)) { for (ReporterIon ptmReporterIon : ptm.getReporterIons()) { HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.REPORTER_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.REPORTER_ION.index, ionsMap); } int subType = ptmReporterIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); ions.add(ptmReporterIon); } } for (NeutralLoss ptmNeutralLoss : ptm.getNeutralLosses()) { boolean found = false; for (NeutralLoss neutralLoss : possibleNeutralLosses) { // @TODO: we keep only different neutral losses. We might want to change that when people // are working with modifications having reproducible motifs like ubiquitin or some glycons. if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { possibleNeutralLosses.add(ptmNeutralLoss); } } if (processedPtms == null) { processedPtms = new ArrayList<String>(); } processedPtms.add(ptmName); } mass += ptm.getMass(); } if (!aminoAcidMasses.contains(mass)) { aminoAcidMasses.add(mass); } // immonium ions HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.IMMONIUM_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.IMMONIUM_ION.index, ionsMap); } ImmoniumIon immoniumIon = new ImmoniumIon(aminoAcid.getSingleLetterCodeAsChar()); int subType = immoniumIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ions.add(immoniumIon); ionsMap.put(subType, ions); } // related ions ionsMap = result.get(Ion.IonType.RELATED_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.RELATED_ION.index, ionsMap); } ArrayList<RelatedIon> relatedIons = RelatedIon.getRelatedIons(aminoAcid); for (RelatedIon tempRelated : relatedIons) { subType = tempRelated.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); } ions.add(tempRelated); ionsMap.put(subType, ions); } } HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { ArrayList<Double> newPatternMassess = new ArrayList<Double>(); if (patternMasses.isEmpty()) { for (double mass : aminoAcidMasses) { int aa = ionNumberOffset + aminoAcidPattern.length() - i; int subaa = aminoAcidPattern.length() - i; double rewindMass = massOffset + mass; double gap = 0; if (massOffset != Atom.O.getMonoisotopicMass()) { gap = massOffset; } // add the x-ions int subType = TagFragmentIon.X_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + Atom.C.getMonoisotopicMass() + 2 * Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the y-ions subType = TagFragmentIon.Y_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + 2 * Atom.H.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the z-ion subType = TagFragmentIon.Z_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass - Atom.N.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } if (!newPatternMassess.contains(mass)) { newPatternMassess.add(mass); } } } else { for (double patternMass : patternMasses) { for (double mass : aminoAcidMasses) { int aa = ionNumberOffset + aminoAcidPattern.length() - i; int subaa = aminoAcidPattern.length() - i; double patternFragmentMass = patternMass + mass; double rewindMass = massOffset + patternFragmentMass; double gap = 0; if (massOffset != Atom.O.getMonoisotopicMass()) { gap = massOffset; } // add the x-ions int subType = TagFragmentIon.X_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + Atom.C.getMonoisotopicMass() + 2 * Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the y-ions subType = TagFragmentIon.Y_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + 2 * Atom.H.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the z-ion subType = TagFragmentIon.Z_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass - Atom.N.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } if (!newPatternMassess.contains(patternFragmentMass)) { newPatternMassess.add(patternFragmentMass); } } } } patternMasses = newPatternMassess; } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { for (double mass : patternMasses) { double newMass = offsetMass + mass; if (!newOffsetMasses.contains(newMass)) { newOffsetMasses.add(newMass); } } } massOffsets = newOffsetMasses; ionNumberOffset += aminoAcidPattern.length(); } else if (tagComponent instanceof AminoAcidSequence) { AminoAcidSequence aminoAcidSequence = (AminoAcidSequence) tagComponent; double sequenceMass = 0; for (int i = aminoAcidSequence.length() - 1; i >= 0; i--) { AminoAcid aminoAcid = aminoAcidSequence.getAminoAcidAt(i); // immonium ions HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.IMMONIUM_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.IMMONIUM_ION.index, ionsMap); } ImmoniumIon immoniumIon = new ImmoniumIon(aminoAcid.getSingleLetterCodeAsChar()); int subType = immoniumIon.getSubType(); ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ions.add(immoniumIon); ionsMap.put(subType, ions); } // related ions ionsMap = result.get(Ion.IonType.RELATED_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.RELATED_ION.index, ionsMap); } ArrayList<RelatedIon> relatedIons = RelatedIon.getRelatedIons(aminoAcid); for (RelatedIon tempRelated : relatedIons) { subType = tempRelated.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(1); } ions.add(tempRelated); ionsMap.put(subType, ions); } double mass = aminoAcid.getMonoisotopicMass(); for (ModificationMatch modificationMatch : aminoAcidSequence.getModificationsAt(i + 1)) { String ptmName = modificationMatch.getTheoreticPtm(); PTM ptm = PTMFactory.getInstance().getPTM(ptmName); if (processedPtms == null || !processedPtms.contains(ptmName)) { for (ReporterIon ptmReporterIon : ptm.getReporterIons()) { ionsMap = result.get(Ion.IonType.REPORTER_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.REPORTER_ION.index, ionsMap); } subType = ptmReporterIon.getSubType(); ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); ions.add(ptmReporterIon); } } for (NeutralLoss ptmNeutralLoss : ptm.getNeutralLosses()) { boolean found = false; for (NeutralLoss neutralLoss : possibleNeutralLosses) { // @TODO: we keep only different neutral losses. We might want to change that when people // are working with modifications having reproducible motifs like ubiquitin or some glycons. if (ptmNeutralLoss.isSameAs(neutralLoss)) { found = true; break; } } if (!found) { possibleNeutralLosses.add(ptmNeutralLoss); } } if (processedPtms == null) { processedPtms = new ArrayList<String>(); } processedPtms.add(ptmName); } mass += ptm.getMass(); } sequenceMass += mass; ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { int aa = ionNumberOffset + aminoAcidSequence.length() - i; int subaa = aminoAcidSequence.length() - i; double rewindMass = massOffset + sequenceMass; double gap = 0; if (massOffset != Atom.O.getMonoisotopicMass()) { gap = massOffset; } // add the x-ions subType = TagFragmentIon.X_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + Atom.C.getMonoisotopicMass() + 2 * Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the y-ions subType = TagFragmentIon.Y_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + 2 * Atom.H.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the z-ion subType = TagFragmentIon.Z_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass - Atom.N.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { double newMass = offsetMass + sequenceMass; if (!newOffsetMasses.contains(newMass)) { newOffsetMasses.add(newMass); } } massOffsets = newOffsetMasses; ionNumberOffset += aminoAcidSequence.length(); } else if (tagComponent instanceof MassGap) { double gapMass = tagComponent.getMass(); int aa = ionNumberOffset; int subaa = 0; HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.TAG_FRAGMENT_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(); result.put(Ion.IonType.TAG_FRAGMENT_ION.index, ionsMap); } for (double massOffset : massOffsets) { double gap = gapMass; if (massOffset != Atom.O.getMonoisotopicMass()) { gap += massOffset; } double rewindMass = massOffset + gapMass; // add the x-ions int subType = TagFragmentIon.X_ION; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + Atom.C.getMonoisotopicMass() + 2 * Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the y-ions subType = TagFragmentIon.Y_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass + 2 * Atom.H.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } // add the z-ion subType = TagFragmentIon.Z_ION; ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new TagFragmentIon(subType, aa, subaa, rewindMass - Atom.N.getMonoisotopicMass() + Atom.O.getMonoisotopicMass() - getLossesMass(losses), losses, gap)); } } ArrayList<Double> newOffsetMasses = new ArrayList<Double>(); for (double offsetMass : massOffsets) { newOffsetMasses.add(offsetMass + gapMass); } massOffsets = newOffsetMasses; ionNumberOffset++; } else { throw new UnsupportedOperationException("Fragment ion not implemented for tag component " + tagComponent.getClass() + "."); } } // add the precursor ion HashMap<Integer, ArrayList<Ion>> ionsMap = result.get(Ion.IonType.PRECURSOR_ION.index); if (ionsMap == null) { ionsMap = new HashMap<Integer, ArrayList<Ion>>(1); result.put(Ion.IonType.PRECURSOR_ION.index, ionsMap); } int subType = PrecursorIon.PRECURSOR; ArrayList<Ion> ions = ionsMap.get(subType); if (ions == null) { ions = new ArrayList<Ion>(neutralLossesCombinations.size()); ionsMap.put(subType, ions); } for (ArrayList<NeutralLoss> losses : neutralLossesCombinations) { ions.add(new PrecursorIon(tag.getMass() - getLossesMass(losses), losses)); } return result; } /** * Convenience method returning the possible neutral losses combination as * accounted by the factory, i.e., for now up to two neutral losses per * peak. * * @param possibleNeutralLosses the possible neutral losses * @return the possible combinations */ public static ArrayList<ArrayList<NeutralLoss>> getAccountedNeutralLosses(ArrayList<NeutralLoss> possibleNeutralLosses) { // We will account for up to two neutral losses per ion maximum ArrayList<ArrayList<NeutralLoss>> neutralLossesCombinations = new ArrayList<ArrayList<NeutralLoss>>(); ArrayList<NeutralLoss> tempList = new ArrayList<NeutralLoss>(0); neutralLossesCombinations.add(tempList); for (NeutralLoss neutralLoss1 : possibleNeutralLosses) { boolean found = false; for (ArrayList<NeutralLoss> accountedCombination : neutralLossesCombinations) { if (accountedCombination.size() == 1 && accountedCombination.get(0).isSameAs(neutralLoss1)) { found = true; } } if (!found) { tempList = new ArrayList<NeutralLoss>(1); tempList.add(neutralLoss1); neutralLossesCombinations.add(tempList); } for (NeutralLoss neutralLoss2 : possibleNeutralLosses) { if (!neutralLoss1.isSameAs(neutralLoss2)) { found = false; for (ArrayList<NeutralLoss> accountedCombination : neutralLossesCombinations) { if (accountedCombination.size() == 2) { if (accountedCombination.get(0).isSameAs(neutralLoss1) && accountedCombination.get(1).isSameAs(neutralLoss2)) { found = true; break; } if (accountedCombination.get(0).isSameAs(neutralLoss2) && accountedCombination.get(1).isSameAs(neutralLoss1)) { found = true; break; } } } if (!found) { tempList = new ArrayList<NeutralLoss>(2); tempList.add(neutralLoss1); tempList.add(neutralLoss2); neutralLossesCombinations.add(tempList); } } } } return neutralLossesCombinations; } /** * Convenience summing the masses of various neutral losses. * * @param neutralLosses list of neutral losses * @return the sum of the masses */ public static double getLossesMass(ArrayList<NeutralLoss> neutralLosses) { double result = 0; for (NeutralLoss neutralLoss : neutralLosses) { result += neutralLoss.getMass(); } return result; } }