Java tutorial
/* * Maui, Maltcms User Interface. * Copyright (C) 2008-2014, The authors of Maui. All rights reserved. * * Project website: http://maltcms.sf.net * * Maui may be used under the terms of either the * * GNU Lesser General Public License (LGPL) * http://www.gnu.org/licenses/lgpl.html * * or the * * Eclipse Public License (EPL) * http://www.eclipse.org/org/documents/epl-v10.php * * As a user/recipient of Maui, you may choose which license to receive the code * under. Certain files or entire directories may not be covered by this * dual license, but are subject to licenses compatible to both LGPL and EPL. * License exceptions are explicitly declared in all relevant files or in a * LICENSE file in the relevant directories. * * Maui 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. Please consult the relevant license documentation * for details. */ package net.sf.maltcms.chromaui.project.spi.descriptors; import cross.Factory; import cross.annotations.Configurable; import cross.cache.ICacheDelegate; import cross.cache.ICacheElementProvider; import cross.datastructures.fragments.IFileFragment; import cross.datastructures.fragments.IVariableFragment; import cross.datastructures.fragments.ImmutableVariableFragment2; import cross.datastructures.fragments.VariableFragment; import cross.datastructures.tuple.Tuple2D; import cross.exception.NotImplementedException; import cross.exception.ResourceNotAvailableException; import java.awt.Point; import java.awt.geom.Rectangle2D; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import lombok.extern.java.Log; import maltcms.datastructures.caches.IScanLine; import maltcms.datastructures.ms.IChromatogram2D; import maltcms.datastructures.ms.IExperiment2D; import maltcms.datastructures.ms.IScan2D; import maltcms.datastructures.ms.Scan2D; import maltcms.tools.MaltcmsTools; import net.sf.maltcms.chromaui.project.spi.caching.ChromatogramScanCache; import org.apache.commons.configuration.Configuration; import ucar.ma2.Array; import ucar.ma2.ArrayShort; import ucar.ma2.MAMath; /** * * Implementation of 2D chromatogram backed by a cache for mass spectra. * * @author Nils Hoffmann */ @Log public class CachingChromatogram2D implements IChromatogram2D, ICacheElementProvider<Integer, SerializableScan2D> { private IFileFragment parent; private final String scanAcquisitionTimeUnit = "seconds"; @Configurable(name = "var.scan_acquisition_time") private String scan_acquisition_time_var = "scan_acquisition_time"; private List<Array> massValues; private List<Array> intensityValues; private IVariableFragment scanAcquisitionTimeVariable; private ICacheDelegate<Integer, SerializableScan2D> whm; private int scans; private boolean initialized = false; private double modulationTime = -1; private double scanRate = 0.0; private RtProvider rtProvider = null; private int prefetchSize = 1000; private int spm = -1; private int modulations = -1; private AtomicBoolean loading = new AtomicBoolean(false); private static ExecutorService prefetchLoader = Executors.newFixedThreadPool(1); private SoftReference<double[]> satArrayReference; private SoftReference<Array> satReference; private Tuple2D<Double, Double> massRange; private Tuple2D<Double, Double> timeRange; private Array msLevel; private Map<Short, List<Integer>> msScanMap; public CachingChromatogram2D(final IFileFragment e) { this.parent = e; String id = e.getUri().toString() + "-2D"; whm = ChromatogramScanCache .createVolatileAutoRetrievalCache(UUID.nameUUIDFromBytes(id.getBytes()).toString(), this); } public void setPrefetchSize(int numberOfScansToLoad) { this.prefetchSize = numberOfScansToLoad; } private void init() { if (!initialized) { final String mz = Factory.getInstance().getConfiguration().getString("var.mass_values", "mass_values"); final String intens = Factory.getInstance().getConfiguration().getString("var.intensity_values", "intensity_values"); final String scan_index = Factory.getInstance().getConfiguration().getString("var.scan_index", "scan_index"); final IVariableFragment index = this.parent.getChild(scan_index); this.scans = MaltcmsTools.getNumberOfScans(this.parent); final IVariableFragment mzV = this.parent.getChild(mz); mzV.setIndex(index); activateCache(mzV); massValues = mzV.getIndexedArray(); final IVariableFragment iV = this.parent.getChild(intens); iV.setIndex(index); activateCache(iV); intensityValues = iV.getIndexedArray(); final String modulation_time = Factory.getInstance().getConfiguration().getString("var.modulation_time", "modulation_time"); modulationTime = parent.getChild(modulation_time).getArray().getDouble(0); try { final String scan_rate = Factory.getInstance().getConfiguration().getString("var.scan_rate", "scan_rate"); scanRate = parent.getChild(scan_rate).getArray().getDouble(0); if (scanRate == 0) { Array satA = this.parent.getChild(scan_acquisition_time_var).getArray(); double s0 = satA.getDouble(0); double s1 = satA.getDouble(1); scanRate = 1.0d / (s1 - s0); } spm = (int) (Math.ceil(modulationTime * scanRate)); modulations = (int) (scans / spm); } catch (ResourceNotAvailableException rnae) { } final String first_column_elution_time = Factory.getInstance().getConfiguration() .getString("var.first_column_elution_time", "first_column_elution_time"); final String second_column_elution_time = Factory.getInstance().getConfiguration() .getString("var.second_column_elution_time", "second_column_elution_time"); scanAcquisitionTimeVariable = this.parent.getChild(scan_acquisition_time_var); try { this.parent.getChild(first_column_elution_time); this.parent.getChild(second_column_elution_time); rtProvider = new ArbitraryModulationRtProvider(first_column_elution_time, second_column_elution_time, parent); } catch (ResourceNotAvailableException rnae) { rtProvider = new UniformModulationRtProvider(scan_acquisition_time_var, parent); // IScanLine scanLine = ScanLineCacheFactory.getDefaultScanLineCache(parent); Logger.getLogger(getClass().getName()).info("Generating first and second column elution time!"); IVariableFragment fcmt = new VariableFragment(this.parent, first_column_elution_time); IVariableFragment scmt = new VariableFragment(this.parent, second_column_elution_time); Array satA = scanAcquisitionTimeVariable.getArray(); Array fceta = Array.factory(satA.getElementType(), satA.getShape()); Array sceta = Array.factory(satA.getElementType(), satA.getShape()); for (int i = 0; i < this.scans; i++) { double[] rts = rtProvider.getRts(i); fceta.setDouble(i, rts[0]); sceta.setDouble(i, rts[1]); } fcmt.setArray(fceta); scmt.setArray(sceta); } try { final String ms_level = Factory.getInstance().getConfiguration().getString("var.ms_level", "ms_level"); IVariableFragment msLevelVar = this.parent.getChild(ms_level); msLevel = msLevelVar.getArray(); msScanMap = new TreeMap<>(); for (int i = 0; i < msLevel.getShape()[0]; i++) { Short msLevelValue = msLevel.getShort(i); if (msLevelValue == 0) { Logger.getLogger(getClass().getName()).info("Correcting msLevelValue of 0 to 1"); msLevelValue = 1; msLevel.setShort(i, msLevelValue); } if (msScanMap.containsKey(msLevelValue)) { List<Integer> scanToScan = msScanMap.get(msLevelValue); scanToScan.add(i); } else { List<Integer> scanToScan = new ArrayList<>(); scanToScan.add(scanToScan.size(), i); msScanMap.put(msLevelValue, scanToScan); } } } catch (ResourceNotAvailableException rnae) { Logger.getLogger(getClass().getName()) .info("Chromatogram has no ms_level variable, assuming all scans are MS1!"); msScanMap = new TreeMap<>(); msLevel = new ArrayShort.D1(this.scans); List<Integer> scanToScan = new ArrayList<>(); for (int i = 0; i < this.scans; i++) { scanToScan.add(i); msLevel.setShort(i, (short) 1); } msScanMap.put((short) 1, scanToScan); } initialized = true; } } protected void activateCache(IVariableFragment ivf) { if (ivf.getParent().getName().toLowerCase().endsWith(".mzml") || ivf.getParent().getName().toLowerCase().endsWith(".mzml.xml")) { Logger.getLogger(CachingChromatogram1D.class.getName()) .info("Not activating cached list on mzml file!"); } else { if (ivf instanceof ImmutableVariableFragment2) { Logger.getLogger(getClass().getName()).log(Level.INFO, "Using cached access on variable: {0}", ivf); ((ImmutableVariableFragment2) ivf).setUseCachedList(true); } if (ivf instanceof VariableFragment) { Logger.getLogger(getClass().getName()).log(Level.INFO, "Using cached access on variable: {0}", ivf); ((VariableFragment) ivf).setUseCachedList(true); } } } // protected Scan2D acquireFromCache(final int i) { // try { // init(); // SerializableScan2D scan = whm.get(i); // if (scan == null) { // scan = provide(i); // whm.put(i, scan); // if (!loading.get()) { // Runnable r = new Runnable() { // @Override // public void run() { // int minBound = Math.max(0, i - prefetchSize); // int maxBound = Math.min(getNumberOfScans(), i + prefetchSize); // for (int j = minBound; j <= maxBound; j++) { // whm.put(Integer.valueOf(j), provide(j)); // } // loading.compareAndSet(true, false); // } // }; // prefetchLoader.submit(r); // } // } // return scan.getScan(); // } catch (java.lang.IndexOutOfBoundsException ex) { // System.err.println("Warning: Could not access scan at index " + i); // return null; // } // } protected Scan2D acquireFromCache(final int i) { try { SerializableScan2D scan = whm.get(i); if (scan == null) { System.err.println("Retrieving scan " + i); if (loading.compareAndSet(false, true)) { System.err.println("Scheduling batched prefetch!"); Runnable r = new Runnable() { @Override public void run() { int minBound = Math.max(0, i - prefetchSize); int maxBound = Math.min(getNumberOfScans(), i + prefetchSize); System.err.println( "Prefetching scans from " + minBound + " to " + maxBound + " into cache!"); for (int j = minBound; j <= maxBound; j++) { whm.put(j, provide(j)); } loading.compareAndSet(true, false); } }; prefetchLoader.submit(r); } scan = whm.get(i); if (scan == null) { scan = provide(i); System.err.println("Putting scan " + i + " into cache!"); whm.put(i, scan); } } else { System.err.println("Retrieved scan " + i + " from cache!"); } return scan.getScan(); } catch (java.lang.IndexOutOfBoundsException ex) { Logger.getLogger(getClass().getName()).log(Level.WARNING, "Warning: Could not access scan at index {0}", i); return null; } } protected Scan2D buildScan(int i) { return acquireFromCache(i); } @Override public void configure(final Configuration cfg) { this.scan_acquisition_time_var = cfg.getString("var.scan_acquisition_time", "scan_acquisition_time"); // this.first_column_elution_time_var = cfg.getString("var.first_column_elution_time"); // this.second_column_elution_time_var = cfg.getString("var.second_column_elution_time"); } @Override public Tuple2D<Double, Double> getTimeRange() { init(); if (timeRange == null) { MAMath.MinMax satMM = MAMath.getMinMax(getScanAcquisitionTime()); timeRange = new Tuple2D<>(satMM.min, satMM.max); } return timeRange; } @Override public Tuple2D<Double, Double> getMassRange() { init(); if (massRange == null) { massRange = MaltcmsTools.getMinMaxMassRange(parent); } return massRange; } @Override public List<Array> getIntensities() { init(); return intensityValues; } @Override public List<Array> getMasses() { init(); return massValues; } /** * @param scan scan index to load */ @Override public Scan2D getScan(final int scan) { init(); return buildScan(scan); } @Override public String getScanAcquisitionTimeUnit() { return this.scanAcquisitionTimeUnit; } public List<Scan2D> getScans() { init(); ArrayList<Scan2D> al = new ArrayList<>(); for (int i = 0; i < getNumberOfScans(); i++) { al.add(buildScan(i)); } return al; } /** * This iterator acts on the underlying collection of scans in * Chromatogram1D, so be careful with concurrent access / modification! */ @Override public Iterator<IScan2D> iterator() { final Iterator<IScan2D> iter = new Iterator<IScan2D>() { private int currentPos = 0; @Override public boolean hasNext() { // init(); if (this.currentPos < getScans().size() - 1) { return true; } return false; } @Override public IScan2D next() { return getScan(this.currentPos++); } @Override public void remove() { throw new UnsupportedOperationException("Can not remove scans with iterator!"); } }; return iter; } public void setExperiment(final IExperiment2D e) { this.parent = e; } /* * (non-Javadoc) * * @see maltcms.datastructures.ms.IChromatogram#getScanAcquisitionTime() */ @Override public Array getScanAcquisitionTime() { init(); Array sat = null; if (satReference == null || satReference.get() == null) { sat = scanAcquisitionTimeVariable.getArray(); satReference = new SoftReference<>(sat); } else { sat = satReference.get(); if (sat == null) { sat = scanAcquisitionTimeVariable.getArray(); satReference = new SoftReference<>(sat); } } return sat; } /* * (non-Javadoc) * * @see maltcms.datastructures.ms.IChromatogram#getNumberOfScans() */ @Override public int getNumberOfScans() { init(); return scans; // return MaltcmsTools.getNumberOfScans(this.parent); } protected double[] getSatArray() { double[] satArray = null; if (satArrayReference == null || satArrayReference.get() == null) { satArray = (double[]) getScanAcquisitionTime().get1DJavaArray(double.class); satArrayReference = new SoftReference<>(satArray); } else { satArray = satArrayReference.get(); } return satArray; } @Override public int getIndexFor(double scan_acquisition_time) { init(); double[] satArray = getSatArray(); int idx = Arrays.binarySearch(satArray, scan_acquisition_time); if (idx >= 0) {// exact hit log.log(Level.FINE, "sat {0}, scan_index {1}", new Object[] { scan_acquisition_time, idx }); return idx; } else {// imprecise hit, find closest element int insertionPosition = (-idx) - 1; if (insertionPosition <= 0) { log.log(Level.WARNING, "Insertion position was {0}, setting to index 0", insertionPosition); } if (insertionPosition >= satArray.length) { log.log(Level.WARNING, "Insertion position was {0}, setting to index {1}", new Object[] { insertionPosition, satArray.length - 1 }); } double current = satArray[Math.min(satArray.length - 1, insertionPosition)]; double previous = satArray[Math.max(0, insertionPosition - 1)]; if (Math.abs(scan_acquisition_time - previous) <= Math.abs(scan_acquisition_time - current)) { int index = Math.max(0, insertionPosition - 1); return index; } else { return insertionPosition; } } } /* * (non-Javadoc) * * @see maltcms.datastructures.ms.IChromatogram#getParent() */ @Override public IFileFragment getParent() { // init(); return this.parent; } @Override public SerializableScan2D provide(Integer k) { init(); final Array masses = massValues.get(k); final Array intens = intensityValues.get(k); final double[] rts = rtProvider.getRts(k); short scanMsLevel = 1; if (msLevel != null) { scanMsLevel = msLevel.getByte(k); } Scan2D s = new Scan2D(masses, intens, k, this.parent.getChild(scan_acquisition_time_var).getArray().getDouble(k), k, k, rts[0], rts[1], scanMsLevel); return new SerializableScan2D(s); } @Override public IScan2D getScan2D(int i, int i1) { throw new UnsupportedOperationException("Not supported yet."); } @Override public int getNumberOfModulations() { init(); return modulations; } @Override public int getNumberOfScansPerModulation() { init(); return spm; } @Override public int getNumberOf2DScans() { init(); return scans; //return this.parent.getChild(this.scan_acquisition_time_var, true).getDimensions()[0].getLength(); } @Override public double getModulationDuration() { init(); return this.modulationTime; } @Override public String getSecondColumnScanAcquisitionTimeUnit() { return "seconds"; } @Override public Point getPointFor(int i) { init(); IScan2D scan2d = getScan(i); return new Point(scan2d.getFirstColumnScanIndex(), scan2d.getSecondColumnScanIndex()); } @Override public Point getPointFor(double d) { init(); IScan2D scan2d = getScan(getIndexFor(d)); return new Point(scan2d.getFirstColumnScanIndex(), scan2d.getSecondColumnScanIndex()); } public RtProvider getRtProvider() { init(); return this.rtProvider; } @Override public Rectangle2D getTimeRange2D() { IScan2D startScan = getScanForMsLevel(0, (short) 1); IScan2D endScan = getScanForMsLevel(getNumberOfScansForMsLevel((short) 1) - 1, (short) 1); double[] startRts = getRtProvider().getRts(0); double[] stopRts = getRtProvider().getRts(getNumberOfScans() - 1); return new Rectangle2D.Double(startScan.getFirstColumnScanAcquisitionTime(), 0, endScan.getFirstColumnScanAcquisitionTime() - startScan.getFirstColumnScanAcquisitionTime(), getModulationDuration()); } @Override public IScanLine getScanLineImpl() { throw new NotImplementedException(); } public abstract class RtProvider { abstract double[] getRts(int idx); } public class UniformModulationRtProvider extends RtProvider { private final double satOffset; private final double modulationTime; private final double scanRate; private final double scanDuration; public UniformModulationRtProvider(String rtVariable, IFileFragment resource) { this.satOffset = resource.getChild(rtVariable).getArray().getDouble(0); modulationTime = resource.getChild("modulation_time").getArray().getDouble(0); this.scanRate = resource.getChild("scan_rate").getArray().getDouble(0); this.scanDuration = 1.0d / this.scanRate; } @Override double[] getRts(int idx) { final int scanspermodulation = (int) (this.scanRate * this.modulationTime); final int scanLineIdx = ((int) idx) / scanspermodulation; double sat1 = satOffset + (scanLineIdx * this.modulationTime); double sat2 = (((((float) idx) % ((float) scanspermodulation))) * this.scanDuration); return new double[] { sat1, sat2 }; } } public class ArbitraryModulationRtProvider extends RtProvider { private final Array rt1, rt2; public ArbitraryModulationRtProvider(String rt1Variable, String rt2Variable, IFileFragment resource) { this.rt1 = resource.getChild(rt1Variable).getArray(); this.rt2 = resource.getChild(rt2Variable).getArray(); } @Override double[] getRts(int idx) { return new double[] { rt1.getDouble(idx), rt2.getDouble(idx) }; } } @Override public int getNumberOfScansForMsLevel(short msLevelValue) { init(); if (msScanMap.containsKey(msLevelValue)) { return msScanMap.get(msLevelValue).size(); } return 0; } @Override public Iterable<IScan2D> subsetByMsLevel(final short msLevel) { Iterable<IScan2D> iterable = new Iterable<IScan2D>() { @Override public Iterator<IScan2D> iterator() { return new Scan2DIterator(msLevel); } }; return iterable; } @Override public Collection<Short> getMsLevels() { init(); List<Short> l = new ArrayList<>(msScanMap.keySet()); Collections.sort(l); return l; } @Override public IScan2D getScanForMsLevel(int i, short level) { init(); if (msScanMap.containsKey(level)) { return getScan(msScanMap.get(level).get(i)); } else { throw new ResourceNotAvailableException("No mass spectra available for fragmentation level " + level + " in chromatogram " + getParent().getUri()); } } @Override public List<Integer> getIndicesOfScansForMsLevel(short level) { init(); if (msScanMap.containsKey(level)) { return Collections.unmodifiableList(msScanMap.get(level)); } else { throw new ResourceNotAvailableException("No mass spectra available for fragmentation level " + level + " in chromatogram " + getParent().getUri()); } } private class Scan2DIterator implements Iterator<IScan2D> { private final int maxScans; private int scan = 0; private short msLevel = 1; public Scan2DIterator(short msLevel) { maxScans = getNumberOfScansForMsLevel(msLevel); this.msLevel = msLevel; } @Override public boolean hasNext() { return scan < maxScans - 1; } @Override public IScan2D next() { return getScanForMsLevel(scan++, msLevel); } @Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } }