/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.track;
import org.apache.log4j.Logger;
import org.broad.igv.Globals;
import org.broad.igv.data.CombinedDataSource;
import org.broad.igv.data.CoverageDataSource;
import org.broad.igv.data.DataSource;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.renderer.DataRange;
import org.broad.igv.session.IGVSessionReader;
import org.broad.igv.session.SubtlyImportant;
import org.broad.igv.util.ResourceLocator;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author jrobinso
*/
@XmlType(factoryMethod = "getNextTrack")
@XmlSeeAlso({CombinedDataSource.class})
public class DataSourceTrack extends DataTrack {
private static Logger log = Logger.getLogger(DataSourceTrack.class);
//All tracks have label "Track", we need to specify the type sometimes
//but still preserve backwards compatibility
@XmlAttribute
protected Class clazz = DataSourceTrack.class;
private DataSource dataSource;
/**
* We often don't have data when the track is constructed,
* need to rescale when data is first loaded
*/
private boolean firstDataLoaded = false;
private boolean rescaleOnFirst = false;
private static final String COMBINED_DATA_SOURCE = "COMBINED_DATA_SOURCE";
@SubtlyImportant
private DataSourceTrack(){
super(null, null, null);
}
public DataSourceTrack(ResourceLocator locator, String id, String name, DataSource dataSource) {
super(locator, id, name);
this.dataSource = dataSource;
if(this.dataSource != null){
setTrackType(dataSource.getTrackType());
List<LocusScore> scores = this.dataSource.getSummaryScoresForRange(Globals.CHR_ALL, -1, -1, 0);
if(scores.size() == 0) rescaleOnFirst = true;
initScale(dataSource, scores);
}
}
void initScale(DataSource dataSource, List<LocusScore> scores){
float min = (float) dataSource.getDataMin();
float max = (float) dataSource.getDataMax();
float baseline = 0;
// If the range is all + numbers set the min to zero
if (min > 0) {
min = 0;
}
for (LocusScore score : scores) {
max = Math.max(max, score.getScore());
}
setDataRange(new DataRange(min, baseline, max));
}
public LoadedDataInterval<List<LocusScore>> getSummaryScores(String chr, int startLocation, int endLocation, int zoom) {
List<LocusScore> tmp = dataSource.getSummaryScoresForRange(chr, startLocation, endLocation, zoom);
if (tmp == null) tmp = Collections.EMPTY_LIST;
if(!firstDataLoaded && rescaleOnFirst){
initScale(dataSource, tmp);
firstDataLoaded = true;
}
return new LoadedDataInterval<>(chr, startLocation, endLocation, zoom, tmp);
}
@Override
public void setWindowFunction(WindowFunction statType) {
clearCaches();
if (dataSource != null) {
dataSource.setWindowFunction(statType);
}
}
public boolean isLogNormalized() {
return dataSource.isLogNormalized();
}
public WindowFunction getWindowFunction() {
//For JAXB session loading/unloading, dataSource might be null, and we need to guard against that
return dataSource != null ? dataSource.getWindowFunction() : null;
}
@Override
public Collection<WindowFunction> getAvailableWindowFunctions() {
return dataSource != null ? dataSource.getAvailableWindowFunctions() : null;
}
@Override
public void restorePersistentState(Node node, int version) throws JAXBException {
super.restorePersistentState(node, version);
if (node.hasChildNodes()) {
NodeList childNodes = node.getChildNodes();
for (int ii = 0; ii < childNodes.getLength(); ii++) {
Node child = childNodes.item(ii);
String nodeName = child.getNodeName();
if (nodeName.contains("#text")) continue;
if (nodeName.equalsIgnoreCase(COMBINED_DATA_SOURCE)) {
dataSource = IGVSessionReader.getJAXBContext().createUnmarshaller().unmarshal(child, CombinedDataSource.class).getValue();
}
}
}
}
public void marshalSource(Marshaller m, Element trackElement) throws JAXBException {
if (dataSource == null) return;
DataSource rawSource = dataSource;
if(rawSource instanceof CombinedDataSource){
JAXBElement element = new JAXBElement<CombinedDataSource>(new QName("", COMBINED_DATA_SOURCE), CombinedDataSource.class,
(CombinedDataSource) rawSource);
m.marshal(element, trackElement);
}
}
public void updateTrackReferences(List<Track> allTracks) {
if (dataSource instanceof CombinedDataSource) {
((CombinedDataSource) dataSource).updateTrackReferences(allTracks);
}
}
@Override
public void dispose() {
super.dispose();
if(dataSource != null) {
dataSource.dispose();
}
}
@SubtlyImportant
@XmlAttribute
private void setNormalize(boolean normalize){
if (dataSource != null && dataSource instanceof CoverageDataSource) {
((CoverageDataSource) dataSource).setNormalize(normalize);
}
}
@SubtlyImportant
private boolean getNormalize(){
if (dataSource != null && dataSource instanceof CoverageDataSource) {
return ((CoverageDataSource) dataSource).getNormalize();
}
return false;
}
@SubtlyImportant
private static DataSourceTrack getNextTrack(){
DataSourceTrack out = (DataSourceTrack) IGVSessionReader.getNextTrack();
if (out == null){
out = new DataSourceTrack();
}
return out;
}
}