org.xmlcml.cml.tools.PeakListTool Maven / Gradle / Ivy
/**
* Copyright 2011 Peter Murray-Rust et. al.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xmlcml.cml.tools;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Nodes;
import nu.xom.ParentNode;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.xmlcml.cml.base.AbstractTool;
import org.xmlcml.cml.base.CMLConstants;
import org.xmlcml.cml.base.CMLElements;
import org.xmlcml.cml.element.CMLAtom;
import org.xmlcml.cml.element.CMLAtomSet;
import org.xmlcml.cml.element.CMLLink;
import org.xmlcml.cml.element.CMLMap;
import org.xmlcml.cml.element.CMLMolecule;
import org.xmlcml.cml.element.CMLPeak;
import org.xmlcml.cml.element.CMLPeakGroup;
import org.xmlcml.cml.element.CMLPeakList;
import org.xmlcml.cml.element.CMLPeakList.Type;
import org.xmlcml.cml.interfacex.PeakOrGroup;
/**
* tool for managing peakList
*
* @author pmr
*
*/
public class PeakListTool extends AbstractTool {
final static Logger LOG = Logger.getLogger(PeakListTool.class);
static {
LOG.setLevel(Level.INFO);
}
CMLPeakList peakList = null;
/** constructor.
* requires molecule to contain and optionally
* @param molecule
* @throws RuntimeException must contain a crystal
*/
public PeakListTool(CMLPeakList peakList) throws RuntimeException {
init();
this.peakList = peakList;
}
void init() {
}
/**
* get angle.
*
* @return the angle or null
*/
public CMLPeakList getPeakList() {
return this.peakList;
}
/** gets PeakListTool associated with peakList.
* if null creates one and sets it in peakList
* @param peakList
* @return tool
*/
public static PeakListTool getOrCreateTool(CMLPeakList peakList) {
PeakListTool peakListTool = null;
if (peakList != null) {
peakListTool = (PeakListTool) peakList.getTool();
if (peakListTool == null) {
peakListTool = new PeakListTool(peakList);
peakList.setTool(peakListTool);
}
}
return peakListTool;
}
/**
* creates map from molecule to peakgroups
* based on Morgan equivalence
* assumes initial moleucle and peakList is aligned
* @param molecule
* @return map
*/
public CMLMap getPeakGroupsFromMorgan(CMLMolecule molecule) {
CMLMap atoms2peaks = this.createAtom2PeakMap(molecule);
CMLMap peaks2peakGroups = new CMLMap();
Morgan morgan = new Morgan(molecule);
List atomSetList = morgan.getAtomSetList();
for (CMLAtomSet atomSet : atomSetList) {
String[] atomIds = atomSet.getAtomIDs();
String[] peakIds = new String[atomIds.length];
String id = "";
for (int i = 0; i < atomIds.length; i++) {
peakIds[i] = atoms2peaks.getToRef(atomIds[i]);
LOG.debug(peakIds[i]+" /"+atomIds[i]);
id += peakIds[i];
}
CMLLink link = new CMLLink();
link.setTo(id);
link.setFromSet(peakIds);
peaks2peakGroups.addLink(link);
}
return peaks2peakGroups;
}
/**
* dreate new PeakList with groups according to morgan equivalences
*
* @param molecule
* @return peakList
*/
public CMLPeakList createPeakListGroupedByMorgan(CMLMolecule molecule) {
CMLMap peakGroupMap = this.getPeakGroupsFromMorgan(molecule);
CMLPeakList groupedPeakList = this.createPeakGroups(peakGroupMap);
return groupedPeakList;
}
/**
* creates a new peakList with peakGroups formed by mapping child peaks.
*
* the map should consist of links from peakRefs to a proposed
* set of new peak groups.
* the map may have two types of link:
* - "fromSet" links in the map correspond to groups of
* single peaks in "this". "to" links are new peakGroups
* in the new peakList
* - "from" links map single peaks to "to" peaks in new peakList.
* normally this is just a copy operation but it could be used
* to sort a peakList
* If a peak is not referenced in a from or fromSet it will be deleted
* If a peak is referenced in more than one from or fromSet it will appear
* twice in the new peakList. This could cause problems
* NOTE: The normal method of use will probably be to
* - copy some peaks retaining heir ids
* - group some other peaks into new new groups, while retaining their
* indivdidual ids
* - delete others
* There is no check at present that this is done, so the user is
* responsible for the id-space of the result.
*
* @param peaks2group map of peaks to groups
* @return new peakList
*
*/
public CMLPeakList createPeakGroups(CMLMap peaks2group) {
CMLPeakList peakList = new CMLPeakList();
CMLElements links = peaks2group.getLinkElements();
for (int i = 0; i < links.size(); i++) {
CMLLink link = (CMLLink) links.get(i);
String from = link.getFrom();
String[] fromSet = link.getFromSet();
String to = link.getTo();
String[] toSet = link.getToSet();
if (toSet != null) {
throw new RuntimeException("Cannot group groups");
}
if (from != null && to != null) {
CMLPeak thisPeak = (CMLPeak) this.getPeakChildById(from);
if (thisPeak == null) {
throw new RuntimeException("No peak with id: "+from);
}
CMLPeak newPeak = new CMLPeak(thisPeak);
newPeak.setId(to);
peakList.addPeak(newPeak);
} else if (fromSet != null && to != null) {
CMLPeakGroup peakGroup = new CMLPeakGroup();
peakGroup.setId(to);
for (String fromG : fromSet) {
CMLPeak thisPeak = (CMLPeak) this.getPeakChildById(fromG);
if (thisPeak == null) {
throw new RuntimeException("No peak with id: "+fromG);
}
CMLPeak newPeak = new CMLPeak(thisPeak);
peakGroup.appendChild(newPeak);
}
peakList.appendChild(peakGroup);
} else {
throw new RuntimeException("Cannot map link: (from|Set)"+from+"|"+fromSet+" (to) "+to);
}
}
return peakList;
}
/** adds atomRefs to peaks or peakGroups
* based on ids of immediate children
* the map is of the form:
* link@from = atomId or link@fromSet = atomIds (as ws-separated string)
* link@to = peakId or peakGroup Id (toSet forbidden as groups should be used)
* not all atoms or peaks need to be linked
* existing atomRefs may be overwritten or retained
* @param atoms2peaks map of links
* @param overwrite existing atomRefs (at present a peak can only have
* 1 atomRefs attribute. This might change later)
*/
public void addAtomRefs(CMLMap atoms2peaks, boolean overwrite) {
for (CMLLink link : atoms2peaks.getLinkElements()) {
String peakId = link.getTo();
if (peakId == null) {
throw new RuntimeException("missing @to on link");
}
String atomId = link.getFrom();
String[] atomIds = link.getFromSet();
CMLPeak peak = (CMLPeak) this.getPeakChildById(peakId);
if (peak == null) {
throw new RuntimeException("no peak for: "+peakId);
}
if (overwrite) {
peak.setAtomRefs(S_EMPTY);
}
if (atomId != null) {
addLink(atomId, peak);
} else if (atomIds != null) {
for (String atomIdd : atomIds) {
addLink(atomIdd, peak);
}
} else {
throw new RuntimeException("Must have @from or @fromSet");
}
}
}
private void addLink(String atomRef, CMLPeak peak) {
Attribute peakAtt = peak.getAtomRefsAttribute();
String atomRefs = (peakAtt == null || peakAtt.equals(S_EMPTY)) ? CMLConstants.S_EMPTY : peakAtt.getValue();
if (!atomRefs.equals(S_EMPTY)) {
atomRefs += CMLConstants.S_SPACE;
}
atomRefs += atomRef;
peak.setAtomRefs(atomRefs);
}
/**
* atoms are in 1-1 relationship with peaks
* @param molecule
* @return the map generated to link atoms
*/
public CMLMap createAtom2PeakMap(CMLMolecule molecule) {
CMLMap atoms2peaks = new CMLMap();
List atoms = molecule.getAtoms();
List peaks = peakList.getPeakChildren();
if (atoms.size() != peaks.size()) {
throw new RuntimeException("atoms and peaks do not match: "+
atoms.size()+ "/"+ peaks.size());
}
for (int i = 0; i < peaks.size(); i++) {
CMLLink link = new CMLLink();
link.setTo(peaks.get(i).getId());
link.setFrom(atoms.get(i).getId());
atoms2peaks.addLink(link);
}
return atoms2peaks;
}
/**
* adds atomRefs from molecule assuming
* atoms are in 1-1 relationship with peaks
* @param molecule
* @return the map generated to link atoms
*/
public CMLMap addAtomRefs(CMLMolecule molecule) {
CMLMap atoms2peaks = this.createAtom2PeakMap(molecule);
this.addAtomRefs(atoms2peaks, true);
return atoms2peaks;
}
/**
* finds all peak/Groups with atomRefs. If any contain any ids
* in atomSet, strips these from atomRefs attribute. If attribute
* is then empty delete it or if delete=true delete the node and
* if no siblings deletes its parent
* @param atomSet of atoms with refs to delete
* @param delete if true deletes peak/Groups with empty attributes
*/
public void removeAtomRefsOnPeaksAndGroups(CMLAtomSet atomSet, boolean delete) {
for (String atomId : atomSet.getAtomIDs()) {
Nodes nodes = peakList.query(".//*[@atomRefs]");
for (int i = 0; i < nodes.size(); i++) {
Element elem = (Element) nodes.get(i);
if (elem instanceof CMLPeakGroup || elem instanceof CMLPeak) {
String atomRefs = CMLConstants.S_SPACE+elem.getAttributeValue("atomRefs")+S_SPACE;
String atomRefs1 = atomRefs.replace(S_SPACE+atomId+S_SPACE, CMLConstants.S_SPACE);
if (!atomRefs1.equals(atomRefs)) {
atomRefs1 = atomRefs1.trim();
elem.removeAttribute(elem.getAttribute("atomRefs"));
if (atomRefs1.length() == 0) {
// delete node
if (delete) {
delete(elem);
}
} else {
// replace with modified attribute
Attribute attribute = new Attribute("atomRefs", atomRefs1);
elem.addAttribute(attribute);
}
}
} else {
}
}
}
}
// detach node. if parent has then no child elements, detach it recursively
private void delete(Element elem) {
ParentNode parent = elem.getParent();
elem.detach();
if (parent instanceof CMLPeakGroup &&
((Element) parent).getChildElements().size() == 0) {
delete((Element)parent);
}
}
/**
* @param atomId
* @return new peakList
*/
public CMLPeakList createPeakListFromPeakChildrenByAtomId(String[] atomId) {
CMLPeakList peakList1 = new CMLPeakList();
Nodes nodes = peakList.cmlQuery("./cml:peak[@atomRefs]");
for (int i = 0; i < nodes.size(); i++) {
CMLPeak peak = (CMLPeak) nodes.get(i);
String[] atomRefs = peak.getAtomRefs();
if (check(atomRefs, atomId)) {
peakList1.addPeak(peak);
}
}
return peakList1;
}
// returns true if any of atomRefs and atomIds match
private boolean check(String[] atomRefs, String[] atomIds) {
boolean check = false;
for (String atomRef : atomRefs) {
for (String atomId : atomIds) {
if (atomRef.equals(atomId)) {
check = true;
break;
}
}
}
return check;
}
/**
* @param id
* @return peak or null
*/
public CMLPeak getPeakChildById(String id) {
CMLPeak peak = null;
if (id != null) {
Nodes nodes = peakList.query("./cml:peak[@id='"+id+"']", CMLConstants.CML_XPATH);
if (nodes.size() > 1) {
throw new RuntimeException("Duplicate peak: "+id);
} else if (nodes.size() == 1) {
peak = (CMLPeak) nodes.get(0);
}
}
return peak;
}
/**
* @param id
* @return peak or null
*/
public CMLPeak getPeakDescendantById(String id) {
CMLPeak peak = null;
if (id != null) {
Nodes nodes = peakList.query(".//cml:peak[@id='"+id+"']", CMLConstants.CML_XPATH);
if (nodes.size() > 1) {
throw new RuntimeException("Dupicate peak: "+id);
} else if (nodes.size() == 1) {
peak = (CMLPeak) nodes.get(0);
}
}
return peak;
}
/**
* @param id
* @return peak or null
*/
public PeakOrGroup getPeakOrGroupChildById(String id) {
PeakOrGroup peak = null;
if (id != null) {
Nodes nodes = peakList.cmlQuery("./cml:peak[@id='"+id+"'] | ./cml:peakGroup[@id='"+id+"']");
if (nodes.size() > 1) {
throw new RuntimeException("Duplicate peak or group: "+id);
} else if (nodes.size() == 1) {
peak = (PeakOrGroup) nodes.get(0);
}
}
return peak;
}
/**
* get list of peaks after sorting
* DOES NOT ALTER THIS
* DOES NOT sort on peakGroups
* @param type (xValue or yValue)
* @return list of peaks as array
*/
public List getSortedPeakChildList(Type type) {
PeakComparator comparator = new PeakComparator(type);
CMLPeak[] peakArray = peakList.getPeakChildren().toArray(new CMLPeak[0]);
Arrays.sort(peakArray, comparator);
List peaks = new ArrayList();
for (CMLPeak peak : peakArray) {
peaks.add(peak);
}
return peaks;
}
}
class PeakComparator implements Comparator {
private CMLPeakList.Type type;
/**
* @param type
*/
public PeakComparator(CMLPeakList.Type type) {
this.type = type;
}
/**
* @param peak1
* @param peak2
* @return -1, 0, 1 (0 if objects are null, no xvalues, etc.)
*/
public int compare(PeakOrGroup peak1, PeakOrGroup peak2) {
int result = 0;
if (peak1 != null && peak2 != null) {
if (type.equals(CMLPeakList.Type.XVALUE)) {
double x1 = peak1.getXValue();
double x2 = peak2.getXValue();
if (!Double.isNaN(x1) && !Double.isNaN(x2)) {
result = (x1 < x2) ? -1 : 1;
}
} else if (type.equals(CMLPeakList.Type.YVALUE)) {
double y1 = peak1.getYValue();
double y2 = peak2.getYValue();
if (!Double.isNaN(y1) && !Double.isNaN(y2)) {
result = (y1 < y2) ? -1 : 1;
}
}
}
return result;
}
};
© 2015 - 2025 Weber Informatics LLC | Privacy Policy