diff --git a/build.gradle b/build.gradle index 69b6bfce07..d348f3f9d8 100644 --- a/build.gradle +++ b/build.gradle @@ -104,17 +104,17 @@ configurations { dependencies { implementation( fileTree(dir: 'lib', include: '*.jar'), // first search on disk (old behavior), then maven repos - [group: 'com.google.code.gson', name: 'gson', version: '2.8.5'], + [group: 'com.google.code.gson', name: 'gson', version: '2.8.9'], [group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'], - [group: 'commons-io', name: 'commons-io', version: '2.6'], - [group: 'org.apache.commons', name: 'commons-compress', version: '1.18'], + [group: 'commons-io', name: 'commons-io', version: '2.7'], + [group: 'org.apache.commons', name: 'commons-compress', version: '1.21'], [group: 'org.xerial.snappy', name: 'snappy-java', version: '1.1.7.3'], [group: 'org.apache.commons', name: 'commons-jexl', version: '2.1.1'], [group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'], - [group: 'com.github.samtools', name: 'htsjdk', version: '3.0.5'], + [group: 'com.github.samtools', name: 'htsjdk', version: '4.0.2'], [group: 'org.swinglabs', name: 'swing-layout', version: '1.0.3'], [group: 'com.formdev', name: 'jide-oss', version: '3.7.12'], - [group: 'com.google.guava', name: 'guava', version: '27.0.1-jre'], + [group: 'com.google.guava', name: 'guava', version: '32.1.3-jre'], [group: 'org.apache.xmlgraphics', name: 'batik-dom', version: '1.11'], [group: 'org.apache.xmlgraphics', name: 'batik-svggen', version: '1.11'], [group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.11'], @@ -128,9 +128,9 @@ dependencies { ) testImplementation( - [group: 'junit', name: 'junit', version: '4.13'], - [group: 'com.sparkjava', name: 'spark-core', version: '2.2'], - [group: 'org.glassfish.jersey.core', name: 'jersey-common', version: '2.22.4'] + [group: 'junit', name: 'junit', version: '4.13.1'], + [group: 'com.sparkjava', name: 'spark-core', version: '2.7.2'], + [group: 'org.glassfish.jersey.core', name: 'jersey-common', version: '2.34'] ) testRuntimeOnly( [group: 'org.junit.vintage', name:'junit-vintage-engine', version:'5.8.2'] diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 6df5e1fe34..4133e5c4f1 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,14 +2,16 @@ exports org.broad.igv; exports org.broad.igv.tools; exports org.broad.igv.ui; + exports org.broad.igv.event; exports org.broad.igv.jbrowse; exports org.broad.igv.logging; exports org.broad.igv.util.liftover; exports org.broad.igv.sam.smrt; + exports org.broad.igv.ui.supdiagram; requires com.google.common; requires commons.math3; - requires gson; + requires com.google.gson; requires htsjdk; requires java.datatransfer; requires java.desktop; diff --git a/src/main/java/org/broad/igv/batch/CommandListener.java b/src/main/java/org/broad/igv/batch/CommandListener.java index 95a6ca76de..e134d4c2a1 100755 --- a/src/main/java/org/broad/igv/batch/CommandListener.java +++ b/src/main/java/org/broad/igv/batch/CommandListener.java @@ -181,7 +181,12 @@ private void processClientSession(CommandExecutor cmdExe) throws IOException { String cmd = inputLine; if (!cmd.contains("/oauthCallback")) { - log.info(cmd); + if (cmd.startsWith("SetAccessToken")) { + log.info(cmd.substring(0,14) + " *****"); + } + else { + log.info(cmd); + } } boolean isHTTP = cmd.startsWith("OPTIONS") || cmd.startsWith("HEAD") || cmd.startsWith("GET"); diff --git a/src/main/java/org/broad/igv/event/AlignmentTrackEvent.java b/src/main/java/org/broad/igv/event/AlignmentTrackEvent.java index c5d8273a6b..c1b4091051 100644 --- a/src/main/java/org/broad/igv/event/AlignmentTrackEvent.java +++ b/src/main/java/org/broad/igv/event/AlignmentTrackEvent.java @@ -29,20 +29,6 @@ * @author Jim Robinson * @date 12/2/11 */ -public class AlignmentTrackEvent { - +public record AlignmentTrackEvent(Type type) implements IGVEvent{ public enum Type {ALLELE_THRESHOLD, RELOAD, REFRESH} - - private Object source; - private Type type; - private boolean booleanValue; - - public AlignmentTrackEvent(Object source, Type type) { - this.source = source; - this.type = type; - } - - public Type getType() { - return type; - } -} +} \ No newline at end of file diff --git a/src/main/java/org/broad/igv/event/DataLoadedEvent.java b/src/main/java/org/broad/igv/event/DataLoadedEvent.java index 93ea991a4e..5d946a03db 100644 --- a/src/main/java/org/broad/igv/event/DataLoadedEvent.java +++ b/src/main/java/org/broad/igv/event/DataLoadedEvent.java @@ -31,15 +31,4 @@ * User: jacob * Date: 2013-Feb-06 */ -public class DataLoadedEvent { - - public final ReferenceFrame referenceFrame; - - public DataLoadedEvent(ReferenceFrame referenceFrame){ - this.referenceFrame = referenceFrame; - } - - public ReferenceFrame getReferenceFrame() { - return referenceFrame; - } -} +public record DataLoadedEvent(ReferenceFrame referenceFrame) implements IGVEvent {} diff --git a/src/main/java/org/broad/igv/event/GenomeChangeEvent.java b/src/main/java/org/broad/igv/event/GenomeChangeEvent.java index 2d4f2f98c5..5213a9128a 100644 --- a/src/main/java/org/broad/igv/event/GenomeChangeEvent.java +++ b/src/main/java/org/broad/igv/event/GenomeChangeEvent.java @@ -31,11 +31,4 @@ /** * Created by jrobinso on 1/7/17. */ -public class GenomeChangeEvent { - - public Genome genome; - - public GenomeChangeEvent(Genome genome) { - this.genome = genome; - } -} +public record GenomeChangeEvent(Genome genome) implements IGVEvent {} diff --git a/src/main/java/org/broad/igv/event/GenomeResetEvent.java b/src/main/java/org/broad/igv/event/GenomeResetEvent.java index b9289e97bf..57cc637379 100644 --- a/src/main/java/org/broad/igv/event/GenomeResetEvent.java +++ b/src/main/java/org/broad/igv/event/GenomeResetEvent.java @@ -3,5 +3,5 @@ /** * Created by jrobinso on 2/7/17. */ -public class GenomeResetEvent { +public final class GenomeResetEvent implements IGVEvent { } diff --git a/src/main/java/org/broad/igv/event/IGVEvent.java b/src/main/java/org/broad/igv/event/IGVEvent.java new file mode 100644 index 0000000000..d77051b4a8 --- /dev/null +++ b/src/main/java/org/broad/igv/event/IGVEvent.java @@ -0,0 +1,23 @@ +package org.broad.igv.event; + +import org.broad.igv.oauth.OAuthProvider; +import org.broad.igv.prefs.PreferencesChangeEvent; +import org.broad.igv.sam.InsertionSelectionEvent; +import org.broad.igv.ui.panel.FrameManager; + +public sealed interface IGVEvent + permits + AlignmentTrackEvent, + DataLoadedEvent, + GenomeChangeEvent, + GenomeResetEvent, + RefreshEvent, + StopEvent, + TrackGroupEvent, + ViewChange, + OAuthProvider.AuthStateEvent, + PreferencesChangeEvent, + InsertionSelectionEvent, + FrameManager.ChangeEvent { + +} diff --git a/src/main/java/org/broad/igv/event/IGVEventBus.java b/src/main/java/org/broad/igv/event/IGVEventBus.java index 55804833bf..d91907823a 100644 --- a/src/main/java/org/broad/igv/event/IGVEventBus.java +++ b/src/main/java/org/broad/igv/event/IGVEventBus.java @@ -36,9 +36,7 @@ */ public class IGVEventBus { - static final Logger log = LogManager.getLogger(IGVEventBus.class); - - Map> observerMap; + final Map, Set> observerMap; private static IGVEventBus instance; @@ -54,13 +52,8 @@ public IGVEventBus() { this.observerMap = new HashMap<>(); } - public synchronized void subscribe(Class eventClass, IGVEventObserver observer) { - - Set observerSet = observerMap.get(eventClass); - if (observerSet == null) { - observerSet = Collections.newSetFromMap(new WeakHashMap()); - observerMap.put(eventClass, observerSet); - } + public synchronized void subscribe(Class eventClass, IGVEventObserver observer) { + Set observerSet = observerMap.computeIfAbsent(eventClass, k -> Collections.newSetFromMap(new WeakHashMap<>())); observerSet.add(observer); } @@ -74,7 +67,7 @@ public synchronized void unsubscribe(IGVEventObserver observer) { } } - public void post(Object event) { + public void post(IGVEvent event) { Set observerSet = observerMap.get(event.getClass()); if (observerSet != null) { // Make a copy in case original is modified during loop diff --git a/src/main/java/org/broad/igv/event/IGVEventObserver.java b/src/main/java/org/broad/igv/event/IGVEventObserver.java index 954c6689f3..43e35b2b6a 100644 --- a/src/main/java/org/broad/igv/event/IGVEventObserver.java +++ b/src/main/java/org/broad/igv/event/IGVEventObserver.java @@ -31,6 +31,6 @@ */ public interface IGVEventObserver { - void receiveEvent(Object event); + void receiveEvent(IGVEvent event); } diff --git a/src/main/java/org/broad/igv/event/ReferenceFrameEvent.java b/src/main/java/org/broad/igv/event/ReferenceFrameEvent.java deleted file mode 100644 index 20fb04fbe7..0000000000 --- a/src/main/java/org/broad/igv/event/ReferenceFrameEvent.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 University of California San Diego - * Author: Jim Robinson - * - * 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.event; - -/** - * Created by jrobinson on 6/3/16. - */ -public class ReferenceFrameEvent { -} diff --git a/src/main/java/org/broad/igv/event/RefreshEvent.java b/src/main/java/org/broad/igv/event/RefreshEvent.java index fea6cb3f30..71ecf24eca 100644 --- a/src/main/java/org/broad/igv/event/RefreshEvent.java +++ b/src/main/java/org/broad/igv/event/RefreshEvent.java @@ -3,5 +3,5 @@ /** * Created by jrobinso on 6/24/17. */ -public class RefreshEvent { +public final class RefreshEvent implements IGVEvent { } diff --git a/src/main/java/org/broad/igv/event/StopEvent.java b/src/main/java/org/broad/igv/event/StopEvent.java index eacd355084..7173041a91 100644 --- a/src/main/java/org/broad/igv/event/StopEvent.java +++ b/src/main/java/org/broad/igv/event/StopEvent.java @@ -3,5 +3,5 @@ /** * Created by jrobinso on 6/24/17. */ -public class StopEvent { +public final class StopEvent implements IGVEvent{ } diff --git a/src/main/java/org/broad/igv/event/TrackGroupEvent.java b/src/main/java/org/broad/igv/event/TrackGroupEvent.java index a1f8e177e9..71cef9e812 100644 --- a/src/main/java/org/broad/igv/event/TrackGroupEvent.java +++ b/src/main/java/org/broad/igv/event/TrackGroupEvent.java @@ -29,13 +29,5 @@ * @author Jim Robinson * @date 10/28/11 */ -public class TrackGroupEvent { - - Object source; - - public TrackGroupEvent(Object source) { - this.source = source; - } - - +public final class TrackGroupEvent implements IGVEvent { } diff --git a/src/main/java/org/broad/igv/event/ViewChange.java b/src/main/java/org/broad/igv/event/ViewChange.java index bec16073dc..bd5d216e84 100644 --- a/src/main/java/org/broad/igv/event/ViewChange.java +++ b/src/main/java/org/broad/igv/event/ViewChange.java @@ -37,46 +37,34 @@ * User: jacob * Date: 2013-Jan-30 */ -public class ViewChange { +public final class ViewChange implements IGVEvent{ public enum Type {ChromosomeChange, LocusChange} - boolean recordHistory = false; - public Type type; - public String chrName; - public double start; - public double end; + final boolean recordHistory; + final public Type type; + final public String chrName; + final public double start; + final public double end; - private ViewChange(Type type) { - this.type = type; - } - - private ViewChange(Type type, String chrName) { - this.type = type; - this.chrName = chrName; - } - - private ViewChange(Type type, String chrName, double start, double end) { + private ViewChange(Type type, String chrName, double start, double end, boolean recordHistory) { this.type = type; this.chrName = chrName; this.start = start; this.end = end; + this.recordHistory = recordHistory; } public boolean recordHistory() { return this.recordHistory; } - public void setRecordHistory(boolean recordHistory) { - this.recordHistory = recordHistory; - } - - public static ViewChange ChromosomeChangeResult(String chrName) { - return new ViewChange(Type.ChromosomeChange, chrName); + public static ViewChange ChromosomeChangeResult(String chrName, boolean recordHistory) { + return new ViewChange(Type.ChromosomeChange, chrName, 0.0, 0.0, recordHistory); } - public static ViewChange LocusChangeResult(String chrName, double start, double end) { - return new ViewChange(Type.LocusChange, chrName, start, end); + public static ViewChange LocusChangeResult(String chrName, double start, double end, boolean recordHistory) { + return new ViewChange(Type.LocusChange, chrName, start, end, recordHistory); } } diff --git a/src/main/java/org/broad/igv/event/ZoomChange.java b/src/main/java/org/broad/igv/event/ZoomChange.java deleted file mode 100644 index 0186533817..0000000000 --- a/src/main/java/org/broad/igv/event/ZoomChange.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.event; - -/** - * Events which either cause or are the result of changes in the zoom level - * User: jacob - * Date: 2013-Jan-30 - */ -public class ZoomChange { - - - /** - * Event indicating that the zoom should change. This event - * will generally be sent by UI components which want to change - * the zoom - */ - public static class Cause{ - //public final int oldZoom; - public final int newZoom; - //public final Object source; - - public Cause(int newZoom){ - //this.oldZoom = oldZoom; - this.newZoom = newZoom; - //this.source = source; - } - } - - /** - * Event dispatched after objects have changed their zoom level, - * generally so UI components can repaint - */ - public static class Result{ - public final int currentZoom; - - public Result(int currentZoom){ - this.currentZoom = currentZoom; - } - } -} diff --git a/src/main/java/org/broad/igv/feature/Cytoband.java b/src/main/java/org/broad/igv/feature/Cytoband.java index 4fe94c8195..c02b5c015f 100644 --- a/src/main/java/org/broad/igv/feature/Cytoband.java +++ b/src/main/java/org/broad/igv/feature/Cytoband.java @@ -26,7 +26,7 @@ package org.broad.igv.feature; -public class Cytoband implements NamedFeature { +public class Cytoband implements IGVNamedFeature { String chromosome; String name; int end; diff --git a/src/main/java/org/broad/igv/feature/FeatureDB.java b/src/main/java/org/broad/igv/feature/FeatureDB.java index d513631db8..85de8229c0 100644 --- a/src/main/java/org/broad/igv/feature/FeatureDB.java +++ b/src/main/java/org/broad/igv/feature/FeatureDB.java @@ -53,11 +53,11 @@ public class FeatureDB { /** * Map for all features other than genes. */ - //private static Map featureMap = new HashMap(10000); - private static Map> featureMap = Collections.synchronizedSortedMap(new TreeMap>()); + //private static Map featureMap = new HashMap(10000); + private static Map> featureMap = Collections.synchronizedSortedMap(new TreeMap>()); private static final int MAX_DUPLICATE_COUNT = 20; - public static void addFeature(NamedFeature feature, Genome genome) { + public static void addFeature(IGVNamedFeature feature, Genome genome) { final String name = feature.getName(); if (name != null && name.length() > 0 && !name.equals(".")) { @@ -81,7 +81,7 @@ public static void addFeature(NamedFeature feature, Genome genome) { } } - public static void removeFeature(NamedFeature feature, Genome genome) { + public static void removeFeature(IGVNamedFeature feature, Genome genome) { final String name = feature.getName(); if (name != null && name.length() > 0 && !name.equals(".")) { @@ -134,7 +134,7 @@ private static void removeByAttributes(IGVFeature igvFeature, Genome genome) { * @param genome The genome which these features belong to. Used for checking chromosomes * @return true if successfully added, false if not */ - static boolean put(String name, NamedFeature feature, Genome genome) { + static boolean put(String name, IGVNamedFeature feature, Genome genome) { String key = name.toUpperCase(); if (!Globals.isHeadless()) { Genome currentGenome = genome != null ? genome : GenomeManager.getInstance().getCurrentGenome(); @@ -144,9 +144,9 @@ static boolean put(String name, NamedFeature feature, Genome genome) { } synchronized (featureMap) { - List currentList = featureMap.get(key); + List currentList = featureMap.get(key); if (currentList == null) { - List newList = new SortedList( + List newList = new SortedList( new ArrayList<>(), FeatureComparator.get(true)); boolean added = newList.add(feature); if (added) { @@ -170,7 +170,7 @@ static boolean put(String name, NamedFeature feature, Genome genome) { Genome currentGenome = GenomeManager.getInstance().getCurrentGenome(); if (currentGenome == null || currentGenome.getChromosome(feature.getChr()) != null) { - NamedFeature currentFeature = featureMap.get(key); + IGVNamedFeature currentFeature = featureMap.get(key); if (currentFeature == null) { featureMap.put(key, feature); } else { @@ -200,7 +200,7 @@ static boolean put(String name, NamedFeature feature, Genome genome) { */ - public static void addFeature(String name, NamedFeature feature, Genome genome) { + public static void addFeature(String name, IGVNamedFeature feature, Genome genome) { put(name.toUpperCase(), feature, genome); } @@ -228,9 +228,9 @@ static int size() { /** * Return a feature with the given name. */ - public static NamedFeature getFeature(String name) { + public static IGVNamedFeature getFeature(String name) { String nm = name.trim().toUpperCase(); - List features = featureMap.get(nm); + List features = featureMap.get(nm); if (features != null) { return features.get(0); @@ -255,9 +255,9 @@ public static NamedFeature getFeature(String name) { * string will be found. * @return */ - static Map> getFeaturesMap(String name) { + static Map> getFeaturesMap(String name) { String nm = name.trim().toUpperCase(); - SortedMap> treeMap = (SortedMap) featureMap; + SortedMap> treeMap = (SortedMap) featureMap; //Search is inclusive to first argument, exclusive to second return treeMap.subMap(nm, nm + Character.MAX_VALUE); } @@ -270,7 +270,7 @@ static Map> getFeaturesMap(String name) { * @return * @see #getFeaturesList(String, int, boolean) */ - public static List getFeaturesList(String name, int limit) { + public static List getFeaturesList(String name, int limit) { return getFeaturesList(name, limit, true); } @@ -283,18 +283,18 @@ public static List getFeaturesList(String name, int limit) { * @param longestOnly Whether to take only the longest feature for each name * @return */ - public static List getFeaturesList(String name, int limit, boolean longestOnly) { + public static List getFeaturesList(String name, int limit, boolean longestOnly) { //Note: We are iterating over submap, this needs //to be synchronized over the main map. synchronized (featureMap) { - Map> resultMap = getFeaturesMap(name); + Map> resultMap = getFeaturesMap(name); Set names = resultMap.keySet(); Iterator nameIter = names.iterator(); - ArrayList features = new ArrayList((Math.min(limit, names.size()))); + ArrayList features = new ArrayList((Math.min(limit, names.size()))); int ii = 0; while (nameIter.hasNext() && ii < limit) { - List subFeats = resultMap.get(nameIter.next()); + List subFeats = resultMap.get(nameIter.next()); if (longestOnly) { features.add(subFeats.get(0)); } else { @@ -328,11 +328,11 @@ public static Map getMutationAA(String name, int proteinP } Map results = new HashMap(); - List possibles = featureMap.get(nm); + List possibles = featureMap.get(nm); if (possibles != null) { synchronized (featureMap) { - for (NamedFeature f : possibles) { + for (IGVNamedFeature f : possibles) { if (!(f instanceof BasicFeature)) { continue; } @@ -381,13 +381,13 @@ public static Map getMutationNT(String name, int startPos } Map results = new HashMap(); - List possibles = featureMap.get(nm); + List possibles = featureMap.get(nm); String tempNT; String brefNT = refNT.toUpperCase(); if (possibles != null) { synchronized (featureMap) { - for (NamedFeature f : possibles) { + for (IGVNamedFeature f : possibles) { if (!(f instanceof BasicFeature)) { continue; } diff --git a/src/main/java/org/broad/igv/feature/IGVFeature.java b/src/main/java/org/broad/igv/feature/IGVFeature.java index 0852468bd3..6e1d02cb8c 100644 --- a/src/main/java/org/broad/igv/feature/IGVFeature.java +++ b/src/main/java/org/broad/igv/feature/IGVFeature.java @@ -29,13 +29,12 @@ import java.awt.*; import java.util.Collections; import java.util.List; -import java.util.Map; /** * Interface for features in IGV annotation tracks (FeatureTrack and derived classes). */ -public interface IGVFeature extends LocusScore, NamedFeature { +public interface IGVFeature extends LocusScore, IGVNamedFeature { default String getIdentifier() { return null; diff --git a/src/main/java/org/broad/igv/feature/NamedFeature.java b/src/main/java/org/broad/igv/feature/IGVNamedFeature.java similarity index 94% rename from src/main/java/org/broad/igv/feature/NamedFeature.java rename to src/main/java/org/broad/igv/feature/IGVNamedFeature.java index 17976f3f5e..b5a44e989e 100644 --- a/src/main/java/org/broad/igv/feature/NamedFeature.java +++ b/src/main/java/org/broad/igv/feature/IGVNamedFeature.java @@ -25,13 +25,11 @@ package org.broad.igv.feature; -import htsjdk.tribble.Feature; - /** * @author jrobinso * @date Sep 16, 2010 */ -public interface NamedFeature extends Feature { +public interface IGVNamedFeature extends htsjdk.tribble.NamedFeature { String getName(); diff --git a/src/main/java/org/broad/igv/feature/Locus.java b/src/main/java/org/broad/igv/feature/Locus.java index bf3d93c912..773e63c350 100644 --- a/src/main/java/org/broad/igv/feature/Locus.java +++ b/src/main/java/org/broad/igv/feature/Locus.java @@ -35,7 +35,7 @@ /** * @author jrobinso */ -public class Locus extends Range implements NamedFeature { +public class Locus extends Range implements IGVNamedFeature { private static NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US); diff --git a/src/main/java/org/broad/igv/feature/genome/load/JsonGenomeLoader.java b/src/main/java/org/broad/igv/feature/genome/load/JsonGenomeLoader.java index 740573b0f0..88c83b4e3a 100644 --- a/src/main/java/org/broad/igv/feature/genome/load/JsonGenomeLoader.java +++ b/src/main/java/org/broad/igv/feature/genome/load/JsonGenomeLoader.java @@ -7,11 +7,11 @@ import org.broad.igv.feature.genome.ChromAliasBB; import org.broad.igv.feature.genome.ChromAliasDefaults; import org.broad.igv.feature.genome.ChromAliasFile; +import org.broad.igv.feature.IGVNamedFeature; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.feature.CytoBandFileParser; import org.broad.igv.feature.FeatureDB; -import org.broad.igv.feature.NamedFeature; import org.broad.igv.feature.genome.Genome; import org.broad.igv.feature.genome.fasta.FastaBlockCompressedSequence; import org.broad.igv.feature.genome.fasta.FastaIndexedSequence; @@ -156,6 +156,32 @@ public GenomeDescriptor loadDescriptor() throws IOException { } } + private void addToFeatureDB(List locators, Genome genome) { + for (ResourceLocator locator : locators) { + try { + FeatureReader featureReader = TribbleFeatureSource.getBasicReader(locator, genome); + CloseableTribbleIterator iter = featureReader.iterator(); + while (iter.hasNext()) { + Feature f = iter.next(); + if (f instanceof IGVNamedFeature) { + FeatureDB.addFeature((IGVNamedFeature) f, genome); + } + } + } catch (IOException e) { + log.error("Error loading " + locator.getPath()); + } + } + } + + private String stripQuotes(String str) { + if(str.startsWith("\"")) { + return str.substring(1, str.length() - 1); // Assume also ends with + } + else { + return str; + } + } + public static class GenomeDescriptor { String id; String name; diff --git a/src/main/java/org/broad/igv/feature/tribble/IGVFeatureReader.java b/src/main/java/org/broad/igv/feature/tribble/IGVFeatureReader.java index da793e7fa7..d5146fdc7d 100644 --- a/src/main/java/org/broad/igv/feature/tribble/IGVFeatureReader.java +++ b/src/main/java/org/broad/igv/feature/tribble/IGVFeatureReader.java @@ -28,6 +28,7 @@ +import htsjdk.samtools.util.CloseableIterator; import htsjdk.tribble.Feature; import htsjdk.tribble.index.Index; @@ -49,7 +50,7 @@ public interface IGVFeatureReader { public Iterator query(final String chr, final int start, final int end) throws IOException; - public Iterator iterator() throws IOException; + public CloseableIterator iterator() throws IOException; public List getSequenceNames(); diff --git a/src/main/java/org/broad/igv/feature/tribble/TribbleReaderWrapper.java b/src/main/java/org/broad/igv/feature/tribble/TribbleReaderWrapper.java index 5a219d4711..84ef6e2d89 100644 --- a/src/main/java/org/broad/igv/feature/tribble/TribbleReaderWrapper.java +++ b/src/main/java/org/broad/igv/feature/tribble/TribbleReaderWrapper.java @@ -25,6 +25,7 @@ package org.broad.igv.feature.tribble; +import htsjdk.samtools.util.CloseableIterator; import htsjdk.tribble.*; import htsjdk.tribble.index.Index; import htsjdk.tribble.index.IndexFactory; @@ -54,9 +55,7 @@ public TribbleReaderWrapper(FeatureReader wrappedReader) { public synchronized Iterator query(String chr, int start, int end) throws IOException { // Tribble iterators must be closed, so we need to copy the features and insure closure before exiting. - CloseableTribbleIterator iter = null; - try { - iter = wrappedReader.query(chr, start + 1, end); + try (CloseableTribbleIterator iter = wrappedReader.query(chr, start + 1, end)) { List featureList = new ArrayList(); while (iter.hasNext()) { Feature f = iter.next(); @@ -69,13 +68,11 @@ public synchronized Iterator query(String chr, int start, int end) thro } } return featureList.iterator(); - } finally { - if(iter != null) iter.close(); } } @Override - public Iterator iterator() throws IOException { + public CloseableIterator iterator() throws IOException { // Note: Technically this is a file handle leak as the "close" method of the tribble iterator is not called. // In practice this is not a problem as the iterator() method is only called by batch programs transversing // the entire file. It is none-the-less a file handle leak that should be addressed at some point. diff --git a/src/main/java/org/broad/igv/jbrowse/Chord.java b/src/main/java/org/broad/igv/jbrowse/Chord.java index 36c1a8c15b..cdbfd7982c 100644 --- a/src/main/java/org/broad/igv/jbrowse/Chord.java +++ b/src/main/java/org/broad/igv/jbrowse/Chord.java @@ -65,7 +65,7 @@ public static List fromSAString(Alignment a) { c.refName = shortName(a.getChr()); c.start = a.getStart(); c.end = a.getEnd(); - c.mate = new Mate(shortName(sa.chr), sa.start, sa.start + sa.getLenOnRef()); + c.mate = new Mate(shortName(sa.chr), sa.start, sa.start + sa.getLengthOnReference()); chords.add(c); } } diff --git a/src/main/java/org/broad/igv/lists/GeneList.java b/src/main/java/org/broad/igv/lists/GeneList.java index 12b91ceedc..d68bca8b74 100644 --- a/src/main/java/org/broad/igv/lists/GeneList.java +++ b/src/main/java/org/broad/igv/lists/GeneList.java @@ -26,7 +26,6 @@ package org.broad.igv.lists; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -43,7 +42,11 @@ public class GeneList { private List loci; public GeneList(String name, String description, String group, List loci) { - init(name, description, group, loci); + this.group = group; + this.description = description; + this.name = name; + //We do this to guarantee that certain operations will be supported + this.loci = loci; } public GeneList(String name, List loci) { @@ -54,14 +57,6 @@ public GeneList() { this.group = GeneListManager.USER_GROUP; } - private void init(String name, String description, String group, List loci) { - this.group = group; - this.description = description; - this.name = name; - //We do this to guarantee that certain operations will be supported - this.loci = loci; - } - public String getName() { return name; } @@ -76,13 +71,13 @@ public int size() { public void add(String gene) { if (loci == null) { - loci = new ArrayList(1); + loci = new ArrayList<>(1); } try { //Can't guarantee that list will support this operation loci.add(gene); } catch (Exception e) { - loci = new ArrayList(loci); + loci = new ArrayList<>(loci); loci.add(gene); } } diff --git a/src/main/java/org/broad/igv/oauth/OAuthProvider.java b/src/main/java/org/broad/igv/oauth/OAuthProvider.java index f352bd6781..9e0734ee5a 100644 --- a/src/main/java/org/broad/igv/oauth/OAuthProvider.java +++ b/src/main/java/org/broad/igv/oauth/OAuthProvider.java @@ -5,8 +5,8 @@ import com.google.gson.JsonArray; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; -import org.broad.igv.Globals; import org.broad.igv.batch.CommandListener; import org.broad.igv.event.IGVEventBus; import org.broad.igv.prefs.PreferencesManager; @@ -21,7 +21,6 @@ import java.awt.*; import java.io.IOException; import java.net.*; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.*; @@ -441,30 +440,8 @@ public String getAuthProvider() { return authProvider; } - public static class AuthStateEvent { - boolean authenticated; - String authProvider; - String userName; - - // Assuming that if this event is called, we are indeed autz/authn'd - public AuthStateEvent(boolean authenticated, String authProvider, String userName) { - this.authenticated = authenticated; - this.authProvider = authProvider; - this.userName = userName; - } - - public boolean isAuthenticated() { - return authenticated; - } - - public String getAuthProvider() { - return authProvider; - } - - public String getUserName() { - return userName; - } - } + // Assuming that if this event is called, we are indeed autz/authn'd + public record AuthStateEvent(boolean authenticated, String authProvider, String userName) implements IGVEvent {} } diff --git a/src/main/java/org/broad/igv/prefs/IGVPreferences.java b/src/main/java/org/broad/igv/prefs/IGVPreferences.java index 96d170f034..9b283869cb 100644 --- a/src/main/java/org/broad/igv/prefs/IGVPreferences.java +++ b/src/main/java/org/broad/igv/prefs/IGVPreferences.java @@ -359,14 +359,14 @@ private void checkForAlignmentChanges(Map updatedPreferenceMap) } if (reloadSAM) { - IGVEventBus.getInstance().post(new AlignmentTrackEvent(this, AlignmentTrackEvent.Type.RELOAD)); + IGVEventBus.getInstance().post(new AlignmentTrackEvent(AlignmentTrackEvent.Type.RELOAD)); } // A reload is harsher than a refresh; only send the weaker request if the stronger one is not sent. if (!reloadSAM && refreshSAM) { - IGVEventBus.getInstance().post(new AlignmentTrackEvent(this, AlignmentTrackEvent.Type.REFRESH)); + IGVEventBus.getInstance().post(new AlignmentTrackEvent(AlignmentTrackEvent.Type.REFRESH)); } if (updatedPreferenceMap.containsKey(SAM_ALLELE_THRESHOLD)) { - IGVEventBus.getInstance().post(new AlignmentTrackEvent(this, AlignmentTrackEvent.Type.ALLELE_THRESHOLD)); + IGVEventBus.getInstance().post(new AlignmentTrackEvent(AlignmentTrackEvent.Type.ALLELE_THRESHOLD)); } } diff --git a/src/main/java/org/broad/igv/prefs/PreferencesChangeEvent.java b/src/main/java/org/broad/igv/prefs/PreferencesChangeEvent.java index e2e5b95d75..8df8511011 100644 --- a/src/main/java/org/broad/igv/prefs/PreferencesChangeEvent.java +++ b/src/main/java/org/broad/igv/prefs/PreferencesChangeEvent.java @@ -1,7 +1,9 @@ package org.broad.igv.prefs; +import org.broad.igv.event.IGVEvent; + /** * Created by jrobinso on 1/22/17. */ -public class PreferencesChangeEvent { +public final class PreferencesChangeEvent implements IGVEvent { } diff --git a/src/main/java/org/broad/igv/prefs/PreferencesManager.java b/src/main/java/org/broad/igv/prefs/PreferencesManager.java index bce0d08f7d..dc7360ebae 100644 --- a/src/main/java/org/broad/igv/prefs/PreferencesManager.java +++ b/src/main/java/org/broad/igv/prefs/PreferencesManager.java @@ -2,6 +2,7 @@ import org.broad.igv.DirectoryManager; import org.broad.igv.Globals; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.logging.LogManager; @@ -368,7 +369,7 @@ private synchronized void storePreferences() { } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof PreferencesChangeEvent) { storePreferences(); } diff --git a/src/main/java/org/broad/igv/sam/AlignmentDataManager.java b/src/main/java/org/broad/igv/sam/AlignmentDataManager.java index 84acfae192..327d06a50d 100644 --- a/src/main/java/org/broad/igv/sam/AlignmentDataManager.java +++ b/src/main/java/org/broad/igv/sam/AlignmentDataManager.java @@ -28,6 +28,7 @@ import org.broad.igv.event.DataLoadedEvent; import org.broad.igv.feature.genome.ChromAlias; import org.broad.igv.feature.genome.ChromAliasManager; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.IGVEventBus; @@ -102,7 +103,7 @@ public AlignmentDataManager(ResourceLocator locator, Genome genome) throws IOExc IGVEventBus.getInstance().subscribe(RefreshEvent.class, this); } - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof FrameManager.ChangeEvent) { trimCache(); } else if (event instanceof RefreshEvent) { @@ -209,7 +210,7 @@ public AlignmentInterval getLoadedInterval(ReferenceFrame frame) { } public AlignmentInterval getLoadedInterval(ReferenceFrame frame, boolean includeOverlaps) { - // Search for interval completely containining reference frame region + // Search for interval completely containing reference frame region for (AlignmentInterval interval : intervalCache) { if (interval.contains(frame.getCurrentRange())) { return interval; @@ -302,8 +303,10 @@ public void load(ReferenceFrame frame, currentlyLoading = null; } + IGVEventBus.getInstance().post(new DataLoadedEvent(frame)); + } /** diff --git a/src/main/java/org/broad/igv/sam/AlignmentRenderer.java b/src/main/java/org/broad/igv/sam/AlignmentRenderer.java index 14460f2127..aae5566bd7 100644 --- a/src/main/java/org/broad/igv/sam/AlignmentRenderer.java +++ b/src/main/java/org/broad/igv/sam/AlignmentRenderer.java @@ -944,10 +944,10 @@ private static void drawClippedEnds(final Graphics2D g, final int[] xPoly, final try { //left side if (drawLeftClip) { - if (sri != null && sri.previous != null) { - final SupplementaryAlignment previous = sri.previous; - if (previous.contigsMatch(sri.alignment)) { - g.setColor(previous.getStrand() == sri.alignment.getReadStrand() ? NON_INVERSION_COLOR : INVERSION_COLOR); + if (sri != null && sri.previous() != null) { + final SupplementaryAlignment previous = sri.previous(); + if (previous.contigsMatch(sri.alignment())) { + g.setColor(previous.getStrand() == sri.alignment().getReadStrand() ? NON_INVERSION_COLOR : INVERSION_COLOR); } else { if (previous.getContig() != null) { g.setColor(ChromosomeColors.getColor(previous.getContig())); @@ -969,11 +969,11 @@ private static void drawClippedEnds(final Graphics2D g, final int[] xPoly, final } //right side if (drawRightClip) { - if (sri != null && sri.next != null) { - final SupplementaryAlignment next = sri.next; - if (next.contigsMatch(sri.alignment)) { + if (sri != null && sri.next() != null) { + final SupplementaryAlignment next = sri.next(); + if (next.contigsMatch(sri.alignment())) { //TODO Set to scaled color by contig position - g.setColor(next.getStrand() == sri.alignment.getReadStrand() ? NON_INVERSION_COLOR : INVERSION_COLOR); + g.setColor(next.getStrand() == sri.alignment().getReadStrand() ? NON_INVERSION_COLOR : INVERSION_COLOR); } else { g.setColor(ChromosomeColors.getColor(next.getContig())); rightContigLabel = next.getContig(); diff --git a/src/main/java/org/broad/igv/sam/AlignmentTileLoader.java b/src/main/java/org/broad/igv/sam/AlignmentTileLoader.java index 1547bd4bfd..cde1606679 100644 --- a/src/main/java/org/broad/igv/sam/AlignmentTileLoader.java +++ b/src/main/java/org/broad/igv/sam/AlignmentTileLoader.java @@ -28,6 +28,7 @@ import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMTag; import htsjdk.samtools.util.CloseableIterator; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.prefs.IGVPreferences; @@ -380,7 +381,7 @@ public boolean isMoleculo() { } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof StopEvent) { cancel = true; reader.cancelQuery(); diff --git a/src/main/java/org/broad/igv/sam/AlignmentTrack.java b/src/main/java/org/broad/igv/sam/AlignmentTrack.java index 4469b705c5..7f8d1c2a0d 100644 --- a/src/main/java/org/broad/igv/sam/AlignmentTrack.java +++ b/src/main/java/org/broad/igv/sam/AlignmentTrack.java @@ -29,6 +29,7 @@ import org.broad.igv.Globals; import org.broad.igv.event.AlignmentTrackEvent; import org.broad.igv.event.DataLoadedEvent; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.feature.FeatureUtils; @@ -53,6 +54,7 @@ import org.broad.igv.ui.panel.IGVPopupMenu; import org.broad.igv.ui.panel.ReferenceFrame; import org.broad.igv.ui.util.MessageUtils; +import org.broad.igv.ui.util.UIUtilities; import org.broad.igv.util.ResourceLocator; import org.broad.igv.util.StringUtils; import org.broad.igv.util.blat.BlatClient; @@ -82,6 +84,14 @@ public class AlignmentTrack extends AbstractTrack implements IGVEventObserver { // Alignment colors static final Color DEFAULT_ALIGNMENT_COLOR = new Color(185, 185, 185); //200, 200, 200); + public static void sortSelectedReadsToTheTop(final Set selectedReadNames) { + //copy this in case it changes out from under us + Set selectedReadNameCopy = new HashSet<>(selectedReadNames); + //Run this on the event thread to make sure it happens after loading begins + UIUtilities.invokeOnEventThread(() -> + IGV.getInstance().sortAlignmentTracks(SortOption.NONE, null, null, false, selectedReadNameCopy)); + } + public enum ColorOption { INSERT_SIZE, READ_STRAND, @@ -188,7 +198,7 @@ enum OrientationType { private static final int GROUP_MARGIN = 5; private static final int TOP_MARGIN = 20; private static final int DS_MARGIN_0 = 2; - private static final int DOWNAMPLED_ROW_HEIGHT = 3; + private static final int DOWNSAMPLED_ROW_HEIGHT = 3; private static final int INSERTION_ROW_HEIGHT = 9; public enum BisulfiteContext { @@ -367,33 +377,27 @@ public void init() { @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof FrameManager.ChangeEvent) { // Trim insertionInterval map to current frames - - } else if (event instanceof AlignmentTrackEvent) { - AlignmentTrackEvent e = (AlignmentTrackEvent) event; - AlignmentTrackEvent.Type eventType = e.getType(); - switch (eventType) { - case ALLELE_THRESHOLD: - dataManager.alleleThresholdChanged(); - break; - case RELOAD: + } else if (event instanceof AlignmentTrackEvent e) { + switch (e.type()) { + case ALLELE_THRESHOLD -> dataManager.alleleThresholdChanged(); + case RELOAD -> { clearCaches(); repaint(); - case REFRESH: - repaint(); - break; + } + case REFRESH -> repaint(); + } + } else if (event instanceof DataLoadedEvent dataLoaded) { + if (dataManager.isLoaded(dataLoaded.referenceFrame())) { + actionToPerformOnFrameLoad.computeIfPresent(dataLoaded.referenceFrame(), (k, v) -> { + v.accept(k); + return null; //remove this action from the map + }); } - - } else if (event instanceof DataLoadedEvent) { - final DataLoadedEvent dataLoaded = (DataLoadedEvent) event; - actionToPerformOnFrameLoad.computeIfPresent(dataLoaded.getReferenceFrame(), (k, v) -> { - v.accept(k); - return null; - }); } } @@ -475,7 +479,7 @@ public int getHeight() { int nGroups = dataManager.getMaxGroupCount(); int h = Math.max(minHeight, getNLevels() * getRowHeight() + nGroups * GROUP_MARGIN + TOP_MARGIN - + DS_MARGIN_0 + DOWNAMPLED_ROW_HEIGHT); + + DS_MARGIN_0 + DOWNSAMPLED_ROW_HEIGHT); return Math.max(minimumHeight, h); } @@ -542,7 +546,7 @@ public void render(RenderContext context, Rectangle rect) { rect.y += DS_MARGIN_0; downsampleRect = new Rectangle(rect); - downsampleRect.height = DOWNAMPLED_ROW_HEIGHT; + downsampleRect.height = DOWNSAMPLED_ROW_HEIGHT; renderDownsampledIntervals(context, downsampleRect); alignmentsRect = new Rectangle(rect); @@ -694,7 +698,7 @@ private void renderAlignments(RenderContext context, Rectangle inputRect) { public void renderExpandedInsertion(InsertionMarker insertionMarker, RenderContext context, Rectangle inputRect) { boolean leaveMargin = getDisplayMode() != DisplayMode.SQUISHED; - inputRect.y += DS_MARGIN_0 + DOWNAMPLED_ROW_HEIGHT + DS_MARGIN_0; + inputRect.y += DS_MARGIN_0 + DOWNSAMPLED_ROW_HEIGHT + DS_MARGIN_0; final AlignmentInterval loadedInterval = dataManager.getLoadedInterval(context.getReferenceFrame(), true); PackedAlignments groups = dataManager.getGroups(loadedInterval, renderOptions); @@ -911,7 +915,7 @@ public boolean handleDataClick(TrackClickEvent te) { return false; } - void setSelectedAlignment(Alignment alignment) { + public void setSelectedAlignment(Alignment alignment) { Color c = readNamePalette.get(alignment.getReadName()); selectedReadNames.put(alignment.getReadName(), c); } diff --git a/src/main/java/org/broad/igv/sam/AlignmentTrackMenu.java b/src/main/java/org/broad/igv/sam/AlignmentTrackMenu.java index 1dfd1d3bce..4ac33bc0f2 100644 --- a/src/main/java/org/broad/igv/sam/AlignmentTrackMenu.java +++ b/src/main/java/org/broad/igv/sam/AlignmentTrackMenu.java @@ -1,18 +1,16 @@ package org.broad.igv.sam; import htsjdk.samtools.SAMTag; -import htsjdk.samtools.util.Locatable; import org.broad.igv.Globals; import org.broad.igv.feature.Locus; import org.broad.igv.feature.Range; import org.broad.igv.feature.Strand; import org.broad.igv.jbrowse.CircularViewUtilities; -import org.broad.igv.lists.GeneList; import org.broad.igv.logging.LogManager; import org.broad.igv.logging.Logger; -import org.broad.igv.prefs.Constants; import org.broad.igv.prefs.PreferencesManager; import org.broad.igv.sam.mods.BaseModficationFilter; +import org.broad.igv.sam.mods.BaseModificationKey; import org.broad.igv.sam.mods.BaseModificationUtils; import org.broad.igv.sashimi.SashimiPlot; import org.broad.igv.tools.PFMExporter; @@ -20,7 +18,7 @@ import org.broad.igv.track.Track; import org.broad.igv.track.TrackClickEvent; import org.broad.igv.track.TrackMenuUtils; -import org.broad.igv.ui.AlignmentDiagramFrame; +import org.broad.igv.ui.supdiagram.SupplementaryAlignmentDiagramDialog; import org.broad.igv.ui.IGV; import org.broad.igv.ui.InsertSizeSettingsDialog; import org.broad.igv.ui.panel.FrameManager; @@ -203,7 +201,7 @@ private void addShowChimericRegions(final AlignmentTrack alignmentTrack, final T try { List supplementaryAlignments = SupplementaryAlignment.parseFromSATag(saTag); alignmentTrack.setSelectedAlignment(clickedAlignment); - addNewLociToFrames(e.getFrame(), supplementaryAlignments, alignmentTrack.getSelectedReadNames().keySet()); + FrameManager.addNewLociToFrames(e.getFrame(), supplementaryAlignments, alignmentTrack.getSelectedReadNames().keySet()); } catch (final Exception ex) { MessageUtils.showMessage("Failed to handle SA tag: " + saTag + " due to " + ex.getMessage()); item.setEnabled(false); @@ -221,7 +219,7 @@ private void addShowDiagram(final TrackClickEvent e, final Alignment clickedAlig item.setEnabled(true); item.addActionListener(aEvt -> { try { - final AlignmentDiagramFrame frame = new AlignmentDiagramFrame(clickedAlignment, new Dimension(500, 100)); + final SupplementaryAlignmentDiagramDialog frame = new SupplementaryAlignmentDiagramDialog(IGV.getInstance().getMainFrame(), clickedAlignment, new Dimension(500, 250)); frame.setVisible(true); } catch (final Exception ex) { MessageUtils.showMessage("Failed to handle SA tag: " + clickedAlignment.getAttribute(SAMTag.SA.name()) + " due to " + ex.getMessage()); @@ -683,7 +681,7 @@ void addColorByMenuItem() { // Base modifications JRadioButtonMenuItem bmMenuItem; - Set allModifications = dataManager.getAllBaseModificationKeys().stream().map(bmKey -> bmKey.getModification()).collect(Collectors.toSet()); + Set allModifications = dataManager.getAllBaseModificationKeys().stream().map(BaseModificationKey::getModification).collect(Collectors.toSet()); if (allModifications.size() > 0) { BaseModficationFilter filter = renderOptions.getBasemodFilter(); boolean groupByStrand = alignmentTrack.getPreferences().getAsBoolean(BASEMOD_GROUP_BY_STRAND); @@ -1228,7 +1226,7 @@ private void gotoMate(final ReferenceFrame frame, Alignment alignment) { int newStart = (int) Math.max(0, (start + (alignment.getEnd() - alignment.getStart()) / 2 - range / 2)); int newEnd = newStart + (int) range; frame.jumpTo(chr, newStart, newEnd); - sortSelectedReadsToTheTop(alignmentTrack.getSelectedReadNames().keySet()); + AlignmentTrack.sortSelectedReadsToTheTop(alignmentTrack.getSelectedReadNames().keySet()); frame.recordHistory(); } else { MessageUtils.showMessage("Alignment does not have mate, or it is not mapped."); @@ -1246,70 +1244,13 @@ private void splitScreenMate(ReferenceFrame frame, Alignment alignment) { ReadMate mate = alignment.getMate(); if (mate != null && mate.isMapped()) { alignmentTrack.setSelectedAlignment(alignment); - addNewLociToFrames(frame, List.of(mate), alignmentTrack.getSelectedReadNames().keySet()); + FrameManager.addNewLociToFrames(frame, List.of(mate), alignmentTrack.getSelectedReadNames().keySet()); } else { MessageUtils.showMessage("Alignment does not have mate, or it is not mapped."); } } } - private static void addNewLociToFrames(final ReferenceFrame frame, final List toIncludeInSplit, final Set selectedReadNames) { - final List newLoci = toIncludeInSplit.stream() - .map(locatable -> getLocusStringForAlignment(frame, locatable)) - .collect(Collectors.toList()); - List loci = createLociList(frame, newLoci); - String listName = String.join(" ", loci); // TODO check the trailing " " was unnecessary - //Need to sort the frames by position - GeneList geneList = new GeneList(listName, loci); - geneList.sort(Comparator.comparing(Locus::fromString, SortOption.POSITION_COMPARATOR)); - IGV.getInstance().getSession().setCurrentGeneList(geneList); - IGV.getInstance().resetFrames(); - - /* - We want the sort to happen after the frame refresh / track loading begins. - This puts the sort onto the event thread so that it happens after loading has already started. - Since loading reads happens asynchronously on a different thread from the event thread, it is likely - that the loading won't be done by the time the sort fires. In that case the sort will be set as the - action to perform when the load is finished - See {@link AlignmentTrack#sortRows(SortOption, Double, String, boolean, Set)} - */ - sortSelectedReadsToTheTop(selectedReadNames); - } - - private static void sortSelectedReadsToTheTop(final Set selectedReadNames) { - //copy this in case it changes out from under us - Set selectedReadNameCopy = new HashSet<>(selectedReadNames); - UIUtilities.invokeOnEventThread(() -> - IGV.getInstance().sortAlignmentTracks(SortOption.NONE, null, null, false, selectedReadNameCopy)); - } - - private static List createLociList(final ReferenceFrame frame, final List lociToAdd) { - final List loci = new ArrayList<>(FrameManager.getFrames().size() + lociToAdd.size()); - if (FrameManager.isGeneListMode()) { - for (ReferenceFrame ref : FrameManager.getFrames()) { - //If the frame-name is a locus, we use it unaltered - //Don't want to reprocess, easy to get off-by-one - String name = ref.getName(); - loci.add(Locus.fromString(name) != null ? name : ref.getFormattedLocusString()); - } - } else { - loci.add(frame.getFormattedLocusString()); - } - loci.addAll(lociToAdd); - return loci; - } - - private static String getLocusStringForAlignment(final ReferenceFrame frame, final Locatable alignment) { - int adjustedMateStart = alignment.getStart() - 1; - - // Generate a locus string for the alignment. Keep the window width (in base pairs) == to the current range - Range range = frame.getCurrentRange(); - int length = range.getLength(); - int start = Math.max(0, adjustedMateStart - length / 2); - int end = start + length; - return Locus.getFormattedLocusString(alignment.getContig(), start, end); - } - /** * Get the most "specific" alignment at the specified location. Specificity refers to the smallest alignment diff --git a/src/main/java/org/broad/igv/sam/ExperimentTypeChangeEvent.java b/src/main/java/org/broad/igv/sam/ExperimentTypeChangeEvent.java deleted file mode 100644 index bd8060ee1a..0000000000 --- a/src/main/java/org/broad/igv/sam/ExperimentTypeChangeEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.broad.igv.sam; - -/** - * Created by jrobinso on 2/10/17. - */ -public class ExperimentTypeChangeEvent { - - public final Object source; - public final AlignmentTrack.ExperimentType type; - - public ExperimentTypeChangeEvent(Object source, AlignmentTrack.ExperimentType type) { - this.source = source; - this.type = type; - } -} diff --git a/src/main/java/org/broad/igv/sam/InsertionSelectionEvent.java b/src/main/java/org/broad/igv/sam/InsertionSelectionEvent.java index a473232132..73e61f1570 100644 --- a/src/main/java/org/broad/igv/sam/InsertionSelectionEvent.java +++ b/src/main/java/org/broad/igv/sam/InsertionSelectionEvent.java @@ -26,14 +26,11 @@ package org.broad.igv.sam; +import org.broad.igv.event.IGVEvent; + /** * Created by jrobinso on 1/12/17. */ -public class InsertionSelectionEvent { - - public final InsertionMarker insertionMarker; +public record InsertionSelectionEvent(InsertionMarker insertionMarker) implements IGVEvent { - public InsertionSelectionEvent(InsertionMarker insertionMarker) { - this.insertionMarker = insertionMarker; - } } diff --git a/src/main/java/org/broad/igv/sam/SAMAlignment.java b/src/main/java/org/broad/igv/sam/SAMAlignment.java index 3ae7d84dea..42d536c155 100644 --- a/src/main/java/org/broad/igv/sam/SAMAlignment.java +++ b/src/main/java/org/broad/igv/sam/SAMAlignment.java @@ -974,7 +974,7 @@ private String getMlTagString(SAMRecord.SAMTagAndValue tag) { private String getSupplAlignmentString() { StringBuilder sb = new StringBuilder(); - sb.append("SupplementaryAlignments"); + sb.append("Supplementary Alignments"); final List supplementaryAlignments = getSupplementaryAlignments(); final int insertionIndex = SupplementaryAlignment.getInsertionIndex(this, supplementaryAlignments); int i = 0; @@ -996,14 +996,13 @@ private String getSupplAlignmentString() { } private String getThisReadDescriptionForSAList() { - return "
" + - "" + return "
" + + "" + chr + ":" + Globals.DECIMAL_FORMAT.format(getAlignmentStart() + 1) + "-" + Globals.DECIMAL_FORMAT.format(getAlignmentEnd()) - + " (" + getReadStrand().toShortString() + ")" + - ""; + + " (" + this.getReadStrand().toShortString() + ") = " + Globals.DECIMAL_FORMAT.format(getLengthOnReference()) + "bp @MAPQ " + this.getMappingQuality() + " NM" + getAttribute(SAMTag.NM) + + "
"; } - public float getScore() { return getMappingQuality(); } diff --git a/src/main/java/org/broad/igv/sam/SupplementaryAlignment.java b/src/main/java/org/broad/igv/sam/SupplementaryAlignment.java index 8991b3e89d..8b7ff53f19 100644 --- a/src/main/java/org/broad/igv/sam/SupplementaryAlignment.java +++ b/src/main/java/org/broad/igv/sam/SupplementaryAlignment.java @@ -1,6 +1,7 @@ package org.broad.igv.sam; import htsjdk.samtools.Cigar; +import htsjdk.samtools.CigarElement; import htsjdk.samtools.SAMTag; import htsjdk.samtools.TextCigarCodec; import htsjdk.samtools.util.Locatable; @@ -15,11 +16,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; import java.util.List; -import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; public class SupplementaryAlignment implements Locatable { private static final Logger log = LogManager.getLogger(SupplementaryAlignment.class); @@ -27,18 +24,15 @@ public class SupplementaryAlignment implements Locatable { public final String chr; public final int start; - public Strand getStrand() { - return strand; - } - private final Strand strand; private final Cigar cigar; public final int mapQ; public final int numMismatches; - //potentially expensive with very long reads, compute it lazily - private Integer lenOnRef = null; + //potentially expensive with very long reads, compute lazily + private Integer lenOnRef = null; //number of reference bases covered + private Integer numberOfAlignedBases = null; //number of bases in read which are aligned public SupplementaryAlignment(String chr, int start, Strand strand, Cigar cigar, int mapQ, int numMismatches){ this.chr = chr; @@ -49,6 +43,10 @@ public SupplementaryAlignment(String chr, int start, Strand strand, Cigar cigar, this.numMismatches = numMismatches; } + public Strand getStrand() { + return strand; + } + public static int getInsertionIndex(final Alignment alignment, final List supplementaryAlignments) { final SupplementaryAlignment alignmentAsSupplementary = new SupplementaryAlignment(null, 0, alignment.getReadStrand(), alignment.getCigar(), 0, 0); //We're just using this as a placeholder for sorting since Alignment @@ -107,15 +105,15 @@ public static SupplementaryNeighbors getAdjacentSupplementaryReads(final Alignme public String printString() { // chr6:43,143,415-43,149,942 (-) @ MAPQ 60 NM 763 // be sure to adjust start by + 1 because SATag is 1 based but IGV internal is 0 based - return chr + ":" + Globals.DECIMAL_FORMAT.format(start + 1) + "-" + Globals.DECIMAL_FORMAT.format(start + getLenOnRef()) - + " (" + strand.toShortString() + ") = " + Globals.DECIMAL_FORMAT.format(getLenOnRef()) + "bp @MAPQ " + mapQ + " NM" + numMismatches; + return chr + ":" + Globals.DECIMAL_FORMAT.format(start + 1) + "-" + Globals.DECIMAL_FORMAT.format(start + getLengthOnReference()) + + " (" + strand.toShortString() + ") = " + Globals.DECIMAL_FORMAT.format(getLengthOnReference()) + "bp @MAPQ " + mapQ + " NM" + numMismatches; } public static List parseFromSATag(String saTag){ return Arrays.stream(Globals.semicolonPattern.split(saTag)) .map(SupplementaryAlignment::fromSingleSaTagRecord) .sorted(LEADING_CLIP_COMPARATOR) - .collect(Collectors.toList()); + .toList(); } public static SupplementaryAlignment fromSingleSaTagRecord(String saTagRecord){ @@ -142,14 +140,10 @@ public int getCountOfLeadingClipping(){ } public static int getCountOfLeadingClipping(final ClippingCounts counts, final Strand strand) { - switch(strand){ - case NONE: // if there's no strand it's not aligned, and we can assume it's in cycle order - case POSITIVE: - return counts.getLeft(); - case NEGATIVE: - return counts.getRight(); - default: throw new IllegalStateException(strand + " is not an expected value for strand"); - } + return switch (strand) { // if there's no strand it's not aligned, and we can assume it's in cycle order + case NONE, POSITIVE -> counts.getLeft(); + case NEGATIVE -> counts.getRight(); + }; } /** @@ -169,109 +163,48 @@ public int getStart() { @Override public int getEnd() { - return start + getLenOnRef(); + return start + getLengthOnReference(); } - public int getLenOnRef() { + @Override + public int getLengthOnReference() { if(lenOnRef == null) { lenOnRef = cigar.getReferenceLength(); } return cigar.getReferenceLength(); } - static class SupplementaryNeighbors { - final Alignment alignment; - final SupplementaryAlignment previous; - final SupplementaryAlignment next; - public SupplementaryNeighbors(Alignment alignment, - SupplementaryAlignment previous, SupplementaryAlignment next) { - this.alignment = alignment; - if(alignment.isNegativeStrand()){ - this.next = previous; - this.previous = next; - } else { - this.next = next; - this.previous = previous; + /** + * get the count of non-clipped bases which are in the read + * this differs from {@link #getLengthOnReference()} because it includes insertions bases but not deletions + */ + public int getNumberOfAlignedBases(){ + if( numberOfAlignedBases == null) { + int length = 0; + for(CigarElement element : cigar) { + switch (element.getOperator()) { + case M, I, EQ, X -> length += element.getLength(); + default -> {} + } } + numberOfAlignedBases = length; } - public SupplementaryAlignment previousIgnoreOrientation (){ - return alignment.isNegativeStrand() ? next : previous; - } - - public SupplementaryAlignment nextIgnoreOrientation (){ - return alignment.isNegativeStrand() ? previous : next; - } - + return numberOfAlignedBases; } - public static class SupplementaryGroup { - private final Alignment alignment; - private final TreeSet readOrder; - private final TreeSet positionOrder; - - private final Adapter adapter; - public SupplementaryGroup(Alignment alignment){ - final List supplementaryAlignments; - if( alignment instanceof SAMAlignment){ - supplementaryAlignments = ((SAMAlignment) alignment).getSupplementaryAlignments(); - } else { - final Object rawSATag = alignment.getAttribute(SAMTag.SA.name()); - supplementaryAlignments = rawSATag == null ? null : new ArrayList<>(parseFromSATag(rawSATag.toString())); + record SupplementaryNeighbors(Alignment alignment, SupplementaryAlignment previous, SupplementaryAlignment next) { + SupplementaryNeighbors(Alignment alignment, SupplementaryAlignment previous, SupplementaryAlignment next) { + this.alignment = alignment; + if (alignment.isNegativeStrand()) { + this.next = previous; + this.previous = next; + } else { + this.next = next; + this.previous = previous; + } } - this.alignment = alignment; - this.adapter = new Adapter(alignment); - final List combined = new ArrayList<>(supplementaryAlignments); - combined.add(adapter); - readOrder = new TreeSet<>(LEADING_CLIP_COMPARATOR); - readOrder.addAll(combined); - positionOrder = new TreeSet<>(SortOption.POSITION_COMPARATOR); - positionOrder.addAll(combined); - } - - public SupplementaryAlignment getNextInRead(SupplementaryAlignment alignment){ - return readOrder.higher(alignment); } - public SupplementaryAlignment getPreviousInRead(SupplementaryAlignment alignment){ - return readOrder.lower(alignment); - } - public SupplementaryAlignment getNextPosition(SupplementaryAlignment alignment){ - return positionOrder.higher(alignment); - } - - public SupplementaryAlignment getPreviousPosition(SupplementaryAlignment alignment){ - return positionOrder.lower(alignment); - } - - public Adapter getAdapter() { - return adapter; - } - - public Iterator iterateInReadOrder(){ - return readOrder.iterator(); - } - - public Iterator iterateInPositionOrder(){ - return positionOrder.iterator(); - } - - public Stream streamInReadOrder(){ - return readOrder.stream(); - } - public Stream streamInPositionOrder(){ - return positionOrder.stream(); - } - - public int size(){ - return readOrder.size(); - } - - private static class Adapter extends SupplementaryAlignment{ - public Adapter(Alignment a) { - super(a.getChr(), a.getStart(), a.getReadStrand(), a.getCigar(), a.getMappingQuality(), a.getAttribute(SAMTag.NM.name()) == null ? 0 : (int)a.getAttribute(SAMTag.NM.name())); - } - } - } } \ No newline at end of file diff --git a/src/main/java/org/broad/igv/sam/SupplementaryGroup.java b/src/main/java/org/broad/igv/sam/SupplementaryGroup.java new file mode 100644 index 0000000000..c9b0b14e69 --- /dev/null +++ b/src/main/java/org/broad/igv/sam/SupplementaryGroup.java @@ -0,0 +1,147 @@ +package org.broad.igv.sam; + +import htsjdk.samtools.SAMTag; +import org.broad.igv.feature.genome.ChromosomeNameComparator; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SupplementaryGroup { + private final List readOrder; + private final List positionOrder; + + private final Adapter primaryRead; + private final Alignment original; + + public SupplementaryGroup(Alignment alignment) { + final List supplementaryAlignments; + if (alignment instanceof SAMAlignment) { + supplementaryAlignments = ((SAMAlignment) alignment).getSupplementaryAlignments(); + } else { + final Object rawSATag = alignment.getAttribute(SAMTag.SA.name()); + supplementaryAlignments = rawSATag == null ? null : new ArrayList<>(SupplementaryAlignment.parseFromSATag(rawSATag.toString())); + } + + this.primaryRead = new Adapter(alignment); + this.original = alignment; + final List combined = (supplementaryAlignments == null || supplementaryAlignments.isEmpty()) + ? new ArrayList<>() + : new ArrayList<>(supplementaryAlignments); + + combined.add(primaryRead); + readOrder = new ArrayList<>(combined); + readOrder.sort(SupplementaryAlignment.LEADING_CLIP_COMPARATOR); + ; + + positionOrder = new ArrayList<>(combined); + positionOrder.sort(SortOption.POSITION_COMPARATOR); + } + + /** + * @return the number of non-clipped bases in the reads. This includes insertion bases but not deletions. + */ + public int getBaseCount() { + return streamInPositionOrder() + .mapToInt(SupplementaryAlignment::getNumberOfAlignedBases) + .sum(); + } + + /** + * @return the readname of this group + */ + public String getReadName() { + return original.getReadName(); + } + + /** + * @return all the unique contigs in this group, sorted + */ + public List getContigs() { + return streamInReadOrder() + .map(SupplementaryAlignment::getContig) + .filter(Objects::nonNull) + .sorted(ChromosomeNameComparator.get()) + .distinct() + .collect(Collectors.toList()); + } + + + /** + * @return the total number of reference bases aligned to this collection of reads + * this includes deletions but not insertions or clips + */ + public int getLengthOnReference() { + return streamInPositionOrder() + .mapToInt(SupplementaryAlignment::getLengthOnReference) + .sum(); + } + + + public SupplementaryAlignment getNextInRead(SupplementaryAlignment alignment) { + final int i = readOrder.indexOf(alignment); + return readOrder.size() > i + 1 ? readOrder.get(i + 1) : null; + } + + public SupplementaryAlignment getPreviousInRead(SupplementaryAlignment alignment) { + final int i = readOrder.indexOf(alignment); + return i > 0 ? readOrder.get(i - 1) : null; + } + + public SupplementaryAlignment getNextPosition(SupplementaryAlignment alignment) { + final int i = positionOrder.indexOf(alignment); + return positionOrder.size() > i + 1 ? readOrder.get(i + 1) : null; + } + + public SupplementaryAlignment getPreviousPosition(SupplementaryAlignment alignment) { + final int i = readOrder.indexOf(alignment); + return i > 0 ? readOrder.get(i - 1) : null; + } + + /** + * {@link SAMAlignment} and {@link SupplementaryAlignment} don't share any useful interface. We use + * and adapter for the read which owns the SA tag (which isn't included in the tag itself) in order to + * include it seamlessly. + * + * @return the adapter wrapping the original read which contains the SA tag this group was built from + */ + public SupplementaryAlignment getPrimaryAlignment() { + return primaryRead; + } + + /** + * @return the alignment this group was created from + */ + public Alignment unwrap() { + return original; + } + + public Iterator iterateInReadOrder() { + return readOrder.iterator(); + } + + public Iterator iterateInPositionOrder() { + return positionOrder.iterator(); + } + + public Stream streamInReadOrder() { + return readOrder.stream(); + } + + public Stream streamInPositionOrder() { + return positionOrder.stream(); + } + + public int size() { + return readOrder.size(); + } + + private static class Adapter extends SupplementaryAlignment { + public Adapter(Alignment a) { + super(a.getChr(), a.getStart(), a.getReadStrand(), a.getCigar(), a.getMappingQuality(), a.getAttribute(SAMTag.NM.name()) == null ? 0 : (int) a.getAttribute(SAMTag.NM.name())); + } + } +} diff --git a/src/main/java/org/broad/igv/sashimi/SashimiPlot.java b/src/main/java/org/broad/igv/sashimi/SashimiPlot.java index c0211c34e5..cd1ef9d626 100644 --- a/src/main/java/org/broad/igv/sashimi/SashimiPlot.java +++ b/src/main/java/org/broad/igv/sashimi/SashimiPlot.java @@ -25,6 +25,7 @@ package org.broad.igv.sashimi; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.event.ViewChange; @@ -246,7 +247,7 @@ private SashimiJunctionRenderer getRenderer(SpliceJunctionTrack spliceJunctionTr } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { repaint(); } diff --git a/src/main/java/org/broad/igv/session/Session.java b/src/main/java/org/broad/igv/session/Session.java index 43706b50ec..0f185f7e73 100644 --- a/src/main/java/org/broad/igv/session/Session.java +++ b/src/main/java/org/broad/igv/session/Session.java @@ -26,6 +26,7 @@ package org.broad.igv.session; import org.broad.igv.Globals; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.event.ViewChange; @@ -127,7 +128,7 @@ public void reset(String path) { } - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof ViewChange) { ViewChange e = (ViewChange) event; if (e.recordHistory()) { diff --git a/src/main/java/org/broad/igv/track/DataTrack.java b/src/main/java/org/broad/igv/track/DataTrack.java index 2340e91de1..61e579b6b6 100644 --- a/src/main/java/org/broad/igv/track/DataTrack.java +++ b/src/main/java/org/broad/igv/track/DataTrack.java @@ -33,6 +33,7 @@ */ package org.broad.igv.track; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.IGVEventBus; @@ -84,11 +85,11 @@ public DataTrack(ResourceLocator locator, String id, String name) { public DataTrack() { } - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof FrameManager.ChangeEvent) { - Collection frames = ((FrameManager.ChangeEvent) event).getFrames(); + Collection frames = ((FrameManager.ChangeEvent) event).frames(); Map>> newCache = Collections.synchronizedMap(new HashMap<>()); for (ReferenceFrame f : frames) { newCache.put(f.getName(), loadedIntervalCache.get(f.getName())); diff --git a/src/main/java/org/broad/igv/track/FeatureTrack.java b/src/main/java/org/broad/igv/track/FeatureTrack.java index 2f0df831ab..3af5ea1414 100644 --- a/src/main/java/org/broad/igv/track/FeatureTrack.java +++ b/src/main/java/org/broad/igv/track/FeatureTrack.java @@ -27,6 +27,7 @@ import htsjdk.tribble.Feature; import htsjdk.tribble.TribbleException; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.DataLoadedEvent; @@ -222,7 +223,7 @@ public void unload() { /** * Called after features are finished loading, which can be asynchronous */ - public void receiveEvent(Object e) { + public void receiveEvent(IGVEvent e) { if (e instanceof DataLoadedEvent) { // DataLoadedEvent event = (DataLoadedEvent) e; // if (IGV.hasInstance()) { diff --git a/src/main/java/org/broad/igv/track/SequenceTrack.java b/src/main/java/org/broad/igv/track/SequenceTrack.java index bf58f9b739..afcfe29bbf 100644 --- a/src/main/java/org/broad/igv/track/SequenceTrack.java +++ b/src/main/java/org/broad/igv/track/SequenceTrack.java @@ -28,6 +28,7 @@ //~--- non-JDK imports -------------------------------------------------------- +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.IGVEventBus; @@ -132,11 +133,11 @@ public static String getReverseComplement(String sequence) { return new String(complement); } - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof FrameManager.ChangeEvent) { // Remove cache for discarded frames. This seems a rather round-about way to do it. - Collection frames = ((FrameManager.ChangeEvent) event).getFrames(); + Collection frames = ((FrameManager.ChangeEvent) event).frames(); Map> newCache = Collections.synchronizedMap(new HashMap<>()); for (ReferenceFrame f : frames) { newCache.put(f.getName(), loadedIntervalCache.get(f.getName())); diff --git a/src/main/java/org/broad/igv/track/TrackClickEvent.java b/src/main/java/org/broad/igv/track/TrackClickEvent.java index 827472fdb3..6215f2ce5c 100644 --- a/src/main/java/org/broad/igv/track/TrackClickEvent.java +++ b/src/main/java/org/broad/igv/track/TrackClickEvent.java @@ -35,9 +35,9 @@ */ public class TrackClickEvent { - private MouseEvent mouseEvent; - private ReferenceFrame frame; - private double chromosomePosition; + private final MouseEvent mouseEvent; + private final ReferenceFrame frame; + private final double chromosomePosition; public TrackClickEvent(MouseEvent mouseEvent, ReferenceFrame frame) { this.mouseEvent = mouseEvent; @@ -56,6 +56,5 @@ public ReferenceFrame getFrame() { public double getChromosomePosition() { return chromosomePosition; - } } diff --git a/src/main/java/org/broad/igv/track/TrackMenuUtils.java b/src/main/java/org/broad/igv/track/TrackMenuUtils.java index 7cf1e9601f..5e887829aa 100644 --- a/src/main/java/org/broad/igv/track/TrackMenuUtils.java +++ b/src/main/java/org/broad/igv/track/TrackMenuUtils.java @@ -1365,12 +1365,7 @@ public static JMenuItem getFeatureNameAttribute(final Collection selected public static JMenuItem getChangeFontSizeItem(final Collection selectedTracks) { // Change track height by attribute JMenuItem item = new JMenuItem("Change Font Size..."); - item.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent evt) { - changeFontSize(selectedTracks); - } - }); + item.addActionListener(evt -> changeFontSize(selectedTracks)); return item; } diff --git a/src/main/java/org/broad/igv/track/TribbleFeatureSource.java b/src/main/java/org/broad/igv/track/TribbleFeatureSource.java index 92fa7ba8d9..c4d2e12925 100644 --- a/src/main/java/org/broad/igv/track/TribbleFeatureSource.java +++ b/src/main/java/org/broad/igv/track/TribbleFeatureSource.java @@ -25,6 +25,7 @@ package org.broad.igv.track; +import htsjdk.samtools.util.CloseableIterator; import htsjdk.tribble.*; import htsjdk.tribble.index.Index; import org.broad.igv.Globals; @@ -325,10 +326,8 @@ private NonIndexedFeatureSource(AbstractFeatureReader basicReader, FeatureCodec super(basicReader, codec, genome); featureMap = new HashMap<>(25); - Iterator iter = null; - try { - iter = reader.iterator(); + try (CloseableIterator iter = reader.iterator()) { while (iter.hasNext()) { Feature f = iter.next(); if (f == null) continue; @@ -336,34 +335,21 @@ private NonIndexedFeatureSource(AbstractFeatureReader basicReader, FeatureCodec String seqName = f.getChr(); String igvChr = genome == null ? seqName : genome.getCanonicalChrName(seqName); - List featureList = featureMap.get(igvChr); - if (featureList == null) { - featureList = new ArrayList(); - featureMap.put(igvChr, featureList); - } + List featureList = featureMap.computeIfAbsent(igvChr, k -> new ArrayList<>()); featureList.add(f); - if (f instanceof org.broad.igv.feature.NamedFeature) FeatureDB.addFeature((org.broad.igv.feature.NamedFeature) f, genome); + if (f instanceof IGVNamedFeature named) FeatureDB.addFeature(named, genome); - if (this.isVCF && f instanceof Variant) { - Variant v = (Variant) f; + if (this.isVCF && f instanceof Variant v) { String chr2 = v.getAttributeAsString("CHR2"); String pos2 = v.getAttributeAsString("END"); if (chr2 != null && pos2 != null) { String mateChr = genome == null ? chr2 : genome.getCanonicalChrName(chr2); MateVariant mate = new MateVariant(mateChr, Integer.parseInt(pos2), v); - featureList = featureMap.get(mateChr); - if (featureList == null) { - featureList = new ArrayList(); - featureMap.put(mateChr, featureList); - } + featureList = featureMap.computeIfAbsent(mateChr, k -> new ArrayList<>()); featureList.add(mate); } } } - } finally { - if (iter instanceof CloseableTribbleIterator) { - ((CloseableTribbleIterator) iter).close(); - } } for (List featureList : featureMap.values()) { diff --git a/src/main/java/org/broad/igv/ui/AlignmentDiagramFrame.java b/src/main/java/org/broad/igv/ui/AlignmentDiagramFrame.java deleted file mode 100644 index c1df4c3feb..0000000000 --- a/src/main/java/org/broad/igv/ui/AlignmentDiagramFrame.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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.ui; - -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SAMRecordSetBuilder; -import htsjdk.samtools.SAMTag; -import htsjdk.samtools.util.Locatable; -import org.broad.igv.feature.Strand; -import org.broad.igv.feature.genome.ChromosomeNameComparator; -import org.broad.igv.sam.*; -import org.broad.igv.ui.color.ColorPalette; -import org.broad.igv.ui.color.ColorUtilities; -import org.broad.igv.util.ChromosomeColors; -import org.broad.igv.util.Pair; - -import javax.swing.*; -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Point2D; -import java.awt.geom.QuadCurve2D; -import java.awt.geom.Rectangle2D; -import java.util.List; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - - -public class AlignmentDiagramFrame extends JFrame { - - public AlignmentDiagramFrame(Alignment alignment, Dimension dimension) { - - setSize(dimension); - - JPanel readPanel = new JPanel(); - BoxLayout boxLayout = new BoxLayout(readPanel, BoxLayout.Y_AXIS); - readPanel.setLayout(boxLayout); - ReadDiagram diagram = new ReadDiagram(alignment); - readPanel.add(diagram); - diagram.setVisible(true); - setAlwaysOnTop(true); - - this.add(readPanel); - - - } - - private static class ReadDiagram extends JComponent { - - public static final int BORDER_GAP = 30; - public static final int BETWEEN_ALIGNMENT_GAP = 15; - public static final int BETWEEN_CONTIG_GAP = 10; - public static final int ALIGNMENT_HEIGHT = 10; - - SupplementaryAlignment.SupplementaryGroup toDraw; - - public ReadDiagram(Alignment alignment) { - this.toDraw = new SupplementaryAlignment.SupplementaryGroup(alignment); - } - - - @Override - protected void paintComponent(final Graphics g) { - super.paintComponent(g); - g.drawString("Hello", 0, 0); - draw((Graphics2D) g, toDraw); - } - - private void draw(final Graphics2D g, SupplementaryAlignment.SupplementaryGroup toDraw) { - int totalWidth = getWidth(); - final List contigs = toDraw.streamInPositionOrder() - .map(SupplementaryAlignment::getContig) - .filter(Objects::nonNull) - .sorted(ChromosomeNameComparator.get()) - .distinct() - .collect(Collectors.toList()); - - final int totalAlignedBases = toDraw.streamInPositionOrder() - .mapToInt(Locatable::getLengthOnReference) - .sum(); - - String lastContig = contigs.get(0); - final int mid = getHeight() / 2; - double lastPosition = BORDER_GAP; - - float alpha = 0.75f; - int type = AlphaComposite.SRC_OVER; - Composite alignmentAlphaComposite = AlphaComposite.getInstance(type, alpha); - g.setComposite(alignmentAlphaComposite); - g.setColor(Color.LIGHT_GRAY); - - Map> positions = new LinkedHashMap<>(); - int arrowPxWidth = 5; - for (SupplementaryAlignment sa : (Iterable) toDraw::iterateInPositionOrder) { - // System.out.print(sa.toString()); - final int length = sa.getLengthOnReference(); - double fractionOfWhole = (double) length / totalAlignedBases; - double availableSpace = totalWidth - (2 * BORDER_GAP + (toDraw.size() - 1) * BETWEEN_ALIGNMENT_GAP + (contigs.size() - 1) * BETWEEN_CONTIG_GAP); - double spaceToUse = fractionOfWhole * availableSpace; - final String newContig = sa.getContig(); - if (lastPosition != BORDER_GAP && !Objects.equals(lastContig, newContig)) { - lastPosition += BETWEEN_CONTIG_GAP; - } - - lastContig = newContig; - int y = mid - ALIGNMENT_HEIGHT; - int h = ALIGNMENT_HEIGHT; - int start = (int) lastPosition; - int end = (int) (lastPosition + spaceToUse); - /* - 1 2 - 0 <|=======|> 3 - 5 4 - */ - final int startAdjusted = start - (sa.getStrand() == Strand.NEGATIVE ? arrowPxWidth : 0); - final int endAdjusted = end + (sa.getStrand() == Strand.POSITIVE ? arrowPxWidth : 0); - positions.put(sa, new Pair<>(startAdjusted, endAdjusted)); - int[] xPoly = {startAdjusted, start, end, endAdjusted, end, start}; - int[] yPoly = {y + h / 2, y, y, y + h / 2, y + h, y + h}; - Polygon blockShape = new Polygon(xPoly, yPoly, xPoly.length); - lastPosition = end; - lastPosition += BETWEEN_ALIGNMENT_GAP; - g.fill(blockShape); - g.draw(blockShape); - if (sa == toDraw.getAdapter()) { - Color original = g.getColor(); - g.setColor(Color.DARK_GRAY); - g.draw(blockShape); - g.setColor(original); - } - } - - toDraw.streamInReadOrder() - .forEachOrdered(sa -> { - SupplementaryAlignment next = toDraw.getNextInRead(sa); - if (next != null) { - final Pair currentPos = positions.get(sa); - final Pair nextPos = positions.get(next); - int from = sa.getStrand() == Strand.NEGATIVE ? currentPos.getFirst() : currentPos.getSecond(); - int to = next.getStrand() == Strand.NEGATIVE ? nextPos.getSecond() : nextPos.getFirst(); - g.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); - int centerX = (from + to) / 2; - int width = Math.abs(from - to); - int height = (int) ((double) width / getWidth() * getHeight()); - int centerY = mid - ALIGNMENT_HEIGHT / 2; - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setColor(Color.GRAY); - g.drawArc(centerX - width / 2, centerY - height / 2, width, height, 0, 180); - drawPoint(g, to, centerY); - } - }); - - positions.keySet(). - stream() - .collect(Collectors.groupingBy(SupplementaryAlignment::getContig)) - .forEach((contig, alignments) -> { - int start = positions.get(alignments.get(0)).getFirst(); - int end = positions.get(alignments.get(alignments.size() - 1)).getSecond(); - g.setColor(ChromosomeColors.getColor(contig)); - final FontMetrics fontMetrics = g.getFontMetrics(); - final int labelWidth = fontMetrics.stringWidth(contig); - // s----label-----e - final int lineY = mid + 15; - final int LABEL_GAP = 2; - if (labelWidth + 2 * LABEL_GAP < end - start) { - final int leftLineEnd = (start + end - labelWidth) / 2 - LABEL_GAP; - final int rightLineStart = (start + end + labelWidth) / 2 + LABEL_GAP; - g.drawLine(start, lineY - 2, start, lineY + 2); - g.drawLine(end, lineY - 2, end, lineY + 2); - g.drawLine(start, lineY, leftLineEnd, lineY); - g.drawString(contig, leftLineEnd + LABEL_GAP, lineY + fontMetrics.getHeight() / 3); - g.drawLine(rightLineStart, lineY, end, lineY); - } else { - g.drawString(contig, start, lineY + fontMetrics.getHeight() / 3); - } - }); - } - - private static void drawPoint(Graphics2D g, int x, int y) { - g.draw(new Ellipse2D.Double(x - 1, y - 1, 2, 2)); - } - - private static void writeContigName(final Graphics g, final String contig, final double x, final int y) { - Color originalColor = g.getColor(); - try { - g.setColor(ChromosomeColors.getColor(contig)); - g.drawString(contig, (int) x, y); - } finally { - g.setColor(originalColor); - } - } - } - - - - public static void main(String[] args){ - final SAMRecordSetBuilder samRecords = new SAMRecordSetBuilder(); - samRecords.addPair("read1", 13, 50255292, 50255400 ); - final List records = new ArrayList<>(samRecords.getRecords()); - - final SAMRecord record = records.get(0); - String read = "m141213_163442_00118_c100750562550000001823151707081585_s1_p0/6849/0_5821\t2064\t14\t50255292\t38\t1302S12M1I5M1I25M1D12M1I7M1I2M1I10M1D4M1I7M1I10M1I6M1I4M1D9M5I4M1I4M2I18M1I9M1I5M1I20M1I13M1I6M1I6M1I16M1I3M1I13M1D9M1I4M2I4M1D14M1I21M1I1M1D5M11I2M1I11M1D10M1I12M2I6M1D3M1D11M1D3M1I3M1I19M1I3M2I6M1I8M1D1M2I1M1I6M1I9M3I2M1I4M1I1M1I10M1D1M1I9M1D6M1D4M2D6M2D7M1D13M1I5M1D6M1D8M1I3M4I3M2D1M1D9M1D25M1I4M1D20M1I18M1I9M1D3M1I2M1I4M1I6M1I5M1I18M1I4M3I17M1I1M1I9M1I11M1I24M1I5M1D14M3I16M1I23M2I14M1I6M1I10M1I10M1I13M2I15M1D10M1I12M1I2M1I6M1I11M1I5M1I4M1I11M1I6M1I9M1I19M1I1M1I29M1D23M1D16M1I23M1D3M1I2M1D9M1I20M2I4M1D19M1I11M1D21M1I5M1I11M1I2M1D2M1D7M1D16M1I4M1I8M1D9M1D18M1I12M1I9M1D5M1D5M1I5M1I5M1I36M1D7M3145S\t*\t0\t0\tATTCAAGGCACCAGCAGATTCTTGTCTGAATAGGGCTTGCCCCTCAAAGATTGGTACCTATAAGGCTGAGGTTGGGTGGCTCATACCTGTAATCCCAGCACTTTGGCGAGGCCAAGGTCGGGGGGATCCTTGGCCCAGGAGATTCGACACCAGCCTGCGGCAACAAGGGAGACCTCAATCTTAAAAAAAAGATGGCACCTTATTGCAGTGTACCTCACGTGGCAACAGGGGCACTCATCCTCATTCATGACACAGAGGCCCCTCGTTGACTTCACTTCCTAAAGGCCACTCTTAAATACCATCCACACTGGGCTATTTAGGTTCCCATATATGAATGTGGGGATAATTAACTGTAGAACCATAGCAGCCGTAACTCATGAAAGAACATTAATAAGTTTAACACCTCAGTGAGTATTTGTTGAGTCGTAGGATTAACAGAACATAAGGTTATTTGTAACAAATTTTTTAAAAAGACCACCAAAAATGGTTTTAGTCATTACTAAACCAACAGTGTCAGCAGGAAAAAAAGAAAATAGTAATTACAGAATTCCCGTAAGACATTACCTGCCATAAACTGGAGCACACAATTCATAACTCAAGCTTGACATAAAAAAATGCTGGTGGCAAAAGTAAAATATTTTTATTGCCTCTATTTTAGCCGTTATTAAAAACATAAACAGGCTGGTGACATGGTGTCTTACAGCCTGTATCCCAACACTTTGGGAGGCCAAGGGCAGGCGGAATTCACGGAGATCAGGACGATTTCAAGACCAGCACTGGCCAACAATGGTTGAACCCCAATTCACCACTTAAAATACCAAAATAAGCCAGGCGTAGATGGCGCATTTGCTTAGTAATCCCAAGCTACTCAGAAGGCTGAGGCGGATAATTGCTTGAATCACGGGAGGTGGCAGGTTGCCAGTGAGCTGAGATTGTGCCACTTGCACTCCAGCCTGGGACAAGAGCCGAGCGACTGCCTCAAAAAAAAAAAAAAAAAAAAAAAAATTCAAAGAGAGGTAAATTTCATGTAAGAAGATATACAGCAAGGCGTGGGCTTGGCTGTACTAGTTAACAAAACAATATGTGTGTAGGCGATGTGTCTGTCTTAGCTGGAAAATATTCGTCAGTGGTTTTGTAACCAGGGTACTTGGCTCTTTAAATGGTGTACTCGATGTTCAACATTATGTACGACCATATTGGTGTATAGATCGTGAGTGTGTTAGGAGTGTGGATGAAATTTATTTGTGGGGTGTCTTCAACAGACTGTTTCCAATTATTCTCTGTGTTGGTTTTTTGTGTGTGTTGAGATGGAGTACTCGCATCTGTCGACCAGGCTGGAGTGCAATGCACAATCTCGGACTCACTGACACAGCTCTGCCTCCAGAGTTCACGACTATTCTGCCTTCAGCCTTCCCAGTAGCTGAGGGGGGACTAACAGAGGCGCTCACCACCACACCCAGGCTAATTTCTTTTGTTATTTTTTTAGTAGAGACAGCGGTTTCACCACGTATAGCCAGGCATGGTTCTCAATTTCCTGACCTTTGTTGATCTGCCTGCCTGGCCTCTCAAAAGATTGCTGGATTACAGGCGTGAAGCCACCGTGCCAGGCCTCTCATTCTTATCTTTTTTTTTATATTAACTCAGTTCCATCAGAATCCAAAGCCTCTCCGCTTAAAATGATACTTCCTAAAGCTCTGTAAACCAAATTGTTATCAGCTAATCGAAGTACTAACCATTCTATTTGGCTTAGCATAAGATACGTGTGTGGATGTGGTAGTGTCTCGTGGTGTGTGTTGGTTGTATTGTGATGGGGGGGTGTCACTTGTCTTGTGTGGTGTACTGTGGTGCCAGTGTGGTTCCTGACTCCAGGAGGGTAATCACCCTAATTAAGAAGCAGGAAGCCACAAAGTAGCCATACCTTAGTCATCATGTGGATCATCTTACAGCAAATCTCTGTTAACTTCATAACGTTATAAACCTCAAGGAACGGAGTATTCTATTTTCTTAATGTTGTCAGAGGCACCCTCTGTCCCACCTCTAGGTTTTCTGGGGCTGTTTCTTCACAGGTTTGGTCTTTGTTTTTCCTTGGTTCTTCCCCTTCTTCCCTTTTTTCTTCTTTGTTTGAACCTGACCAGACTTTAATTCAGTAGAGGAAAACATCAGTCCAGTGTTGTATTTTTCAATACTGTGGGAATATTTTTACAAGATGTATTGAATTGCCTGTCATACGGCATAGGCAGGTGGGCTAGATGGCCCATTTTGAAAAAGCAAAAGAAAAAAATTGTTTTTAGAATGTTATTTACCCCCAGCATACCTTCATGATAAGTTCACGGTCTTCTTCATCTGGTCTTTGTATTTTTCTTTCATTTTTTCATTTGACTCTTACAAATTCAGAAGATTTTGGTTGAATATTTCAGCAGCAAAAAAAATGTCAACAAATACTTTTGCAAGAAATGGTGTTACTTTATTATTCTCTGTCAAAATGCTTACTGAATAAGGCAACCATATATAATAAACATATTGAGGCTACAAGTTGGATAACTGAAGGTCCTTCATCTTCAGGAATTTACATAGGGTGGAGAGAAGATTACACAGAGAAGTTAAGAATACTTAACAAACATTAAAGAAACTGTTGAGGAAGAAATAAAGTAAAACAGTATTGGAAAAGCAGTCTAATCCTCCTGCCTCAGCCTCCGAGTAGCTGGGGACTACAAGCGCTGCTGCCATGCCCGGCTAATTTTCTGTTATTTTTTAGTAGAGATGGGGTTATTCCACTGTATTAGCCAGGATGGTCTCCAATCTCCTGACTTGTGATTGCCCGCCTCGCCTCCCAAATTGCTGCGGATTTAACAGCGATGAGCCACCACTGCACCGGCCAAAAGTTTTTAAAAAATATAGTCTCAAGAAAGCATAACCATACTGTTAGTTGTGTTTAACTACTAAAAAATGAGGGGGTCCAAAATTTGGACTTCTGAGCAACTAAACAAAATAGAAACATGATCGTTTATATGGATCTTTCCAATGAGCCTATTAACTTTTCTCCTTCTTTAAATCCAATGGTTTAATTTTCAATATTTTCATTTATCAAGTGATAACATTAAGTAAACAAAAATTAAAATCACATTTTTTACCTTAGGTTGTGAGCTGATTGTGTCCTCCAAAAGATAGTGATTGAGGACAATCTCCACGTTTACCTATGAAGTGTGGCCTTATTTGGTTGATAGTGTCTTTCAGATGTAAGCAAGTTAAAATGATATTCTATACTGGATTAGGGTGGGCCCTTAAATCCAACAACTTCTGACATTATGAGAAAGGCATGTTGGGAAGAACATCGAGACAGAAATACACAATTGAAACATGAAATTGTTGACAGAGCAGGACTTGCCATGCTCACTGTAGCCAGGAAATGCTGAGGACCACCCGCAGCCACAGGAAGCTCCACAGAGGACAAGAAGAATTTTCCTCTACCAAGAACCTTCAAGAAAAGCCAGGTTCTTGTGGTGGGCGTGCTTTCATTCATACTTGCAGGTCTTCAAAACTGTGAGGCAATAATTTCTGTTGCTTTTAAAGCCACTCGGTTTGTGGTCATTTGCCACGCGCCTAGGAATCAAAACTAACTATCAACTGGCCAACTTTCGGTTGTTTTTATTTATTTTTTATTTTCATTTTTATTTTATTTTTTTATTTGTTTGAGATGGAGTCTCCCTTGTTGCCCCAGCTGGAGTGCAGAGTGGGCTATGATCCTCAAGCTCACTTGCAACCCCATGGGCCTCCTGGGTTCAAGTGATTCTCCTTGCTCAGCTCCTGAGTAGCTGGGATATACAGGCACACGCCACCATGCGCGGCTAAATTTTTTGTATTTGAGTAGGACGAGGGGTTCACCTGTTGGCCCAAGGCTTTGGATTCTCGAAACTCCTGACGTCAAAAGTGGATCCGCCCACCTTGGCTTCCAAAAGTGCTGGATTTACAGGCGAAAAGCCACTGCACCCAGCCTGTTTTTAATTAACACCATTTTGGTTTGAAATGCTAGAAGGAATTGGGACATTTAACATATACTTTCATTAGAGAGTAATTTTTTTATTGTAAGATATTAATTTTCTGTTTTTCTTTTTCATCATCTCTTTTTTCTAAAGTTTTCAATGTTGAGCTTGTATATCTTTTTGCAATAACAAAAACTTTCATTTAAAAACACTATTGTCACCATTATTTAACATTTTTCTCGTAAGTTTCTAGCGGTAAGTGGCTAAGAAATGGATGTCGGGTTTGATTTTAACAGCTCCCTGGGTCGGGGAGAACAGGGTTGGACTTGAAATCACAGGAAACATGAATCTTCAGTCCCAAGCTGTGCCATTTCCAGCTGTATGATCTTGAGTAAGCCACTGAACTTTTCGTAAACCTCAAGTTTCCCCCACCTCCCTATGATGTTGTTTCTATTGGGCTCTTCAATTTTCTACCACCGTGTGCCCTTTAAAGAAGGATGAGAGCAAGAGGGAAACAAATGTCAAACATTAATTGCATTTTTAATACAGTTCACTGGAGGATGACCCTGGGGAAGAGGGATTGAGGGAAATCAATTCTGGTGATGAAAACAATAATCGAGAATTACAAGAAGGGTCACAGCTTTAAATCTGGATTAGAGAGAAAAATGCCTCTGCATTTTTCTTTGCCTTTGTGGGGTTCAGTCAGACTGTTACTTGATTCAATGTTTACTGTGATTCAAACAGTTCTGAGGGGAAATGCTTTGTGTGTCATCAACTATTACAGACACTGAGGGGACAGAGATGTTCCAGACATAACCCTGTTTTCCTTCATGTGGGCTTCTCTTGTGAGTGGGTGAAGGGTGGGGTCAAAGTACTAGATGGATTTTTTTTAAATTTTTTATATTGTACTTTAAGTTTTAGGGGTACATGTGCACAAATGTACAGGTTTGTTACATATATTTACATGTGCCCATGTTTGGTGTGCTGCAACCATTAACTCGTCAGTTAACATTTAGGGTTATAATCTCCTAATGCTATTCTACCCCGCTCCCGCACCCATAACAGGCTTCTGTTATTGATGTTTCCCTTTTCTGTGTCCATGTGTTTCTCATTGTTCAATTCCACCTGTTGAGTGAGACATGCCAGTGTTTTTGGTTTTTTGTCCTTGTTTGATGTTTGCGGAGAATGAATGGTTTTCCAGCTTCATCCATGTCCCTACAAAGACATGAAAATTCATTTAAGATGGATTTTTTTAAATGACAAAGCAGAATCAGAAAAAATGGCTAAGAGAGGATGAATTGGCTGAGCCTTCTTTTCCATGCATTGGTGTCACCTTTATAAAGGATTGTGAAAATGTCAAGGCAGGGCAGCTGATCTTTTATTTATTGGTTTTCATTACAGGTGGACCTGGCCAGAAGCCCTTGGGAAACTGGCGGTGCGCTACGGGTAATTTATTTCTAGGCACCTTCAAAGGAGAAAAGGGCAGTGTCCTCCCTTCTCAACTGGATATTGTGCTAGACCATAGTTGCATACCGCCCACAAGCCCCCAAGCATCAGCTGGCCTGGCATCCCTGCCCAGAAGGGCCCATTGCCACTGGTCTTTCCACAAAAACCTGAGCCTTAGGCCAAATTAAAAAGCCACTTGTAGGCATTTTTACCACTTGAATTTACAGCCAAGAGGAAAATCCAACCATGGGGAAAAAGATCCACTGGCTTTTGGCAATTTTCCAAACTTTAAAAACCCCCAAAAGGGCCAAAAAAGGCAAAAACTACCCCAAAGGCCAAAAAGGCTATTAGGAACATTACAAGGAAAAAAAGTCTTTGATATTTCTTTTCTTTGT\t*\tSA:Z:17,64042266,-,2676S17M1D10M1I14M1D26M1I2M1I20M2I1M1I23M1I11M1D8M1D10M1D16M1I3M1I3M1D1M1I4M1I11M1I2M1I6M1D10M1I11M1D3M1D11M1D8M3I2M1I14M1I7M4I12M1I3M1I9M1D13M1D23M1I4M1I13M1D12M1I38M1I11M1I5M1D11M1I17M1I14M1D12M1D8M1I2M1I3M1D12M1I2M2I8M1I19M1D1M2D11M1D25M1I2M1I21M1I28M1I4M1D4M1I1M2I4M1I22M1I5M1I3M1D3M1I9M2I6M1I9M1D2M1D4M1D11M1D2M1I18M1D2M1I13M1I23M1I17M1I7M1I2M1D4M1I21M1I25M1D12M1I3M1I29M1D2M1D1M1D6M1D8M1I8M1D17M1D13M1I54M1D5M1I4M1D12M2I1M1I3M1I5M1I3M1I7M1I9M1I1M2I25M1I2M1D5M1D18M1I27M1I2M1I8M1D9M1D3M1I11M1D6M1I2M1I4M2I3M2I5M1I13M2I4M1I30M1D3M1I9M1I30M2D4M1D4M1D9M1D5M1I5M1I3M2I6M1I15M1D20M1I3M1I13M5I18M2I10M1I7M1D1M1I38M1D39M1I5M1I6M1588S,36,188;14,50254065,-,2S19M1D7M1I2M1D8M2I9M1I8M1I1M1I6M1I2M1I2M1D32M1I12M1I7M1D5M1D9M1I15M1I7M1D11M1I5M1I28M1I13M1I12M1I15M1I2M1D1M1I6M1I19M1D2M1D1M1D5M1I8M1I9M1I22M1D3M2D6M1I6M1D5M1I9M1I2M1I1M1I22M2I10M1D12M1I6M1I24M1I4M1I5M2I4M1I5M4I2M1I6M1D8M1I10M2D7M1I8M1D18M1I7M1I4M1D5M1I9M1I2M1D6M1I12M1I18M1D27M1I1M1D7M1D1M1D11M1D15M1I13M1I1M1I3M1I9M1I6M1D23M1I9M1I1M1I3M1D12M1I1M2I12M1I9M1I4M1I2M1D6M1I1M1I33M1I9M2I5M1I8M1I20M1D18M1I10M1I6M1I22M1I16M1I6M3I3M2D9M5I23M1I3M1D10M1D21M1I5M1I7M1I8M1I14M7I8M4I1M1I15M1I5M3D31M1I15M1I26M4624S,38,166;8,123906302,+,157S3M1I2M1I8M1I2M1I2M1I3M1I4M1D14M1I2M2I3M1I1M1I7M1I5M1D13M2I3M1I7M1I2M1I9M2D5M1D11M1I2M1D10M2I4M1I3M1I5M1D5M1I4M1I3M1D4M1I5M1I45M1I4M1I12M1D3M1I1M1I4M2I27M1I3M1D4M1I3M1I5M1I2M1D4M1I6M1D13M1I12M1I9M1I11M1I18M2I4M1I12M1D7M1I40M2I3M1I1M1I8M1D26M1I6M1I15M1D3M2I16M2I7M1I5M1D8M1I6M1D16M1I22M1I6M1I3M1D1M1D3M1I10M1D6M1D10M1I1M1D16M1I3M1I1M1I3M1I35M1I5M1I33M1I13M1I22M2I22M1D5M1I1M1D4M1D21M1I15M1I3M1I5M1I9M1D3M1I10M1D13M1D2M1I4M1I3M1D8M2I5M1D2M3I3M1D20M2I10M1I16M1I5M1I1M2I4M2I2M1I14M1D22M1I7M1I4M1I13M1I1M1I5M1D5M1D22M1I7M1I13M1I17M1I15M1I26M1I1M1D11M2D8M1D20M1I7M1D4M1I8M1D8M4409S,30,166;8,126480844,-,4239S17M1D6M1I30M1I11M1D4M1I7M1I11M1I9M1I50M1I8M1I10M1411S,2,11;\tKB:f:24.508675\tSB:f:24.508675\tID:i:150690\tMD:Z:24C15G1^G31^C31^A101C24^T17^G36^G11T6^C28^C3^T11^G9A30A1^C19C0A3T4C1T2^A2T3A1T1^C3A2^A0C3^CC0A1T1A1^AA0T0A3T1^A0C14T0T1^A1C4^A0A0A2C9^GG1^A9^T29^C47^G13G92C2^C115A5^A125^C23^T11T10A16^G5^A33^A30^A39^A2^A7^A28^A9^C39^A5^A51^A7\tQE:i:2676\tXE:i:1426\tXI:f:0.8567\tNM:i:203\tXR:i:1374\tAS:i:1426\tQS:i:1302\tXS:i:0\tCV:f:23.60419\tSV:i:2"; - String saTag = "17,64042266,-,2676S17M1D10M1I14M1D26M1I2M1I20M2I1M1I23M1I11M1D8M1D10M1D16M1I3M1I3M1D1M1I4M1I11M1I2M1I6M1D10M1I11M1D3M1D11M1D8M3I2M1I14M1I7M4I12M1I3M1I9M1D13M1D23M1I4M1I13M1D12M1I38M1I11M1I5M1D11M1I17M1I14M1D12M1D8M1I2M1I3M1D12M1I2M2I8M1I19M1D1M2D11M1D25M1I2M1I21M1I28M1I4M1D4M1I1M2I4M1I22M1I5M1I3M1D3M1I9M2I6M1I9M1D2M1D4M1D11M1D2M1I18M1D2M1I13M1I23M1I17M1I7M1I2M1D4M1I21M1I25M1D12M1I3M1I29M1D2M1D1M1D6M1D8M1I8M1D17M1D13M1I54M1D5M1I4M1D12M2I1M1I3M1I5M1I3M1I7M1I9M1I1M2I25M1I2M1D5M1D18M1I27M1I2M1I8M1D9M1D3M1I11M1D6M1I2M1I4M2I3M2I5M1I13M2I4M1I30M1D3M1I9M1I30M2D4M1D4M1D9M1D5M1I5M1I3M2I6M1I15M1D20M1I3M1I13M5I18M2I10M1I7M1D1M1I38M1D39M1I5M1I6M1588S,36,188;14,50254065,-,2S19M1D7M1I2M1D8M2I9M1I8M1I1M1I6M1I2M1I2M1D32M1I12M1I7M1D5M1D9M1I15M1I7M1D11M1I5M1I28M1I13M1I12M1I15M1I2M1D1M1I6M1I19M1D2M1D1M1D5M1I8M1I9M1I22M1D3M2D6M1I6M1D5M1I9M1I2M1I1M1I22M2I10M1D12M1I6M1I24M1I4M1I5M2I4M1I5M4I2M1I6M1D8M1I10M2D7M1I8M1D18M1I7M1I4M1D5M1I9M1I2M1D6M1I12M1I18M1D27M1I1M1D7M1D1M1D11M1D15M1I13M1I1M1I3M1I9M1I6M1D23M1I9M1I1M1I3M1D12M1I1M2I12M1I9M1I4M1I2M1D6M1I1M1I33M1I9M2I5M1I8M1I20M1D18M1I10M1I6M1I22M1I16M1I6M3I3M2D9M5I23M1I3M1D10M1D21M1I5M1I7M1I8M1I14M7I8M4I1M1I15M1I5M3D31M1I15M1I26M4624S,38,166;8,123906302,+,157S3M1I2M1I8M1I2M1I2M1I3M1I4M1D14M1I2M2I3M1I1M1I7M1I5M1D13M2I3M1I7M1I2M1I9M2D5M1D11M1I2M1D10M2I4M1I3M1I5M1D5M1I4M1I3M1D4M1I5M1I45M1I4M1I12M1D3M1I1M1I4M2I27M1I3M1D4M1I3M1I5M1I2M1D4M1I6M1D13M1I12M1I9M1I11M1I18M2I4M1I12M1D7M1I40M2I3M1I1M1I8M1D26M1I6M1I15M1D3M2I16M2I7M1I5M1D8M1I6M1D16M1I22M1I6M1I3M1D1M1D3M1I10M1D6M1D10M1I1M1D16M1I3M1I1M1I3M1I35M1I5M1I33M1I13M1I22M2I22M1D5M1I1M1D4M1D21M1I15M1I3M1I5M1I9M1D3M1I10M1D13M1D2M1I4M1I3M1D8M2I5M1D2M3I3M1D20M2I10M1I16M1I5M1I1M2I4M2I2M1I14M1D22M1I7M1I4M1I13M1I1M1I5M1D5M1D22M1I7M1I13M1I17M1I15M1I26M1I1M1D11M2D8M1D20M1I7M1D4M1I8M1D8M4409S,30,166;8,126480844,-,4239S17M1D6M1I30M1I11M1D4M1I7M1I11M1I9M1I50M1I8M1I10M1411S,2,11"; - record.setAttribute(SAMTag.SA, saTag); - final Alignment samAlignment = new SAMAlignment(record); - - AlignmentDiagramFrame frame = new AlignmentDiagramFrame(samAlignment, new Dimension(500, 100)); - frame.setVisible(true); - - } -} diff --git a/src/main/java/org/broad/igv/ui/IGV.java b/src/main/java/org/broad/igv/ui/IGV.java index 193bdcfe52..6ac6e964d9 100644 --- a/src/main/java/org/broad/igv/ui/IGV.java +++ b/src/main/java/org/broad/igv/ui/IGV.java @@ -484,7 +484,7 @@ final public void doViewPreferences() { try { PreferencesEditor.open(this.mainFrame); } catch (Exception e) { - log.error("Error openining preference dialog", e); + log.error("Error opening preference dialog", e); } } @@ -1802,7 +1802,7 @@ public void setGroupByAttribute(String attributeName) { session.setGroupByAttribute(attributeName); resetGroups(); // Some tracks need to respond to changes in grouping, fire notification event - IGVEventBus.getInstance().post(new TrackGroupEvent(this)); + IGVEventBus.getInstance().post(new TrackGroupEvent()); } @@ -2152,12 +2152,12 @@ public boolean waitForNotify(long timeout) { * * @param event */ - public void postEvent(Object event) { + public void postEvent(IGVEvent event) { IGVEventBus.getInstance().post(event); } - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof ViewChange || event instanceof InsertionSelectionEvent) { repaint(); } else if (event instanceof GenomeChangeEvent) { diff --git a/src/main/java/org/broad/igv/ui/IGVMenuBar.java b/src/main/java/org/broad/igv/ui/IGVMenuBar.java index 7cbb546066..68b9e264c8 100644 --- a/src/main/java/org/broad/igv/ui/IGVMenuBar.java +++ b/src/main/java/org/broad/igv/ui/IGVMenuBar.java @@ -32,11 +32,11 @@ import org.broad.igv.batch.CommandExecutor; import org.broad.igv.charts.ScatterPlotUtils; import org.broad.igv.event.GenomeChangeEvent; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.feature.genome.GenomeManager; import org.broad.igv.feature.genome.GenomeUtils; -import org.broad.igv.prefs.IGVPreferences; import org.broad.igv.track.AttributeManager; import org.broad.igv.track.Track; import org.broad.igv.util.GoogleUtils; @@ -1163,10 +1163,10 @@ static void destroyInstance() { @Override - public void receiveEvent(final Object event) { + public void receiveEvent(final IGVEvent event) { if (event instanceof GenomeChangeEvent) { - UIUtilities.invokeOnEventThread(() -> encodeMenuItem.setVisible(EncodeFileBrowser.genomeSupported(((GenomeChangeEvent) event).genome.getId()))); + UIUtilities.invokeOnEventThread(() -> encodeMenuItem.setVisible(EncodeFileBrowser.genomeSupported(((GenomeChangeEvent) event).genome().getId()))); } } diff --git a/src/main/java/org/broad/igv/ui/TooltipTextFrame.java b/src/main/java/org/broad/igv/ui/TooltipTextFrame.java index 3994ebfbb3..5ddd57dc65 100644 --- a/src/main/java/org/broad/igv/ui/TooltipTextFrame.java +++ b/src/main/java/org/broad/igv/ui/TooltipTextFrame.java @@ -88,8 +88,8 @@ public TooltipTextFrame(String title, String text) throws HeadlessException { int w = (int) (1.2 * d.width); int h = (int) (1.25 * d.height); - h = h > 600 ? 600 : (h < 100 ? 100 : h); - w = w > 800 ? 800 : (w < 100 ? 100 : w); + h = h > 600 ? 600 : (Math.max(h, 100)); + w = w > 800 ? 800 : (Math.max(w, 100)); setSize(w, h); diff --git a/src/main/java/org/broad/igv/ui/action/SearchCommand.java b/src/main/java/org/broad/igv/ui/action/SearchCommand.java index 9bfa25ad1f..e4d2aa6d2c 100644 --- a/src/main/java/org/broad/igv/ui/action/SearchCommand.java +++ b/src/main/java/org/broad/igv/ui/action/SearchCommand.java @@ -49,7 +49,6 @@ import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; -import java.io.IOException; import java.net.URL; import java.util.List; import java.util.*; @@ -336,7 +335,7 @@ Set checkTokenType(String token) { */ private SearchResult parseToken(String token) { - List features; + List features; //Guess at token type via regex. //We don't assume success @@ -392,7 +391,7 @@ private SearchResult parseToken(String token) { } } else if (types.contains(ResultType.FEATURE)) { //Check if we have an exact name for the feature name - NamedFeature feat = searchFeatureDBs(token); + IGVNamedFeature feat = searchFeatureDBs(token); if (feat != null) { return new SearchResult(feat); } @@ -400,8 +399,8 @@ private SearchResult parseToken(String token) { return null; } - private NamedFeature searchFeatureDBs(String str) { - NamedFeature feat = FeatureDB.getFeature(str.toUpperCase().trim()); + private IGVNamedFeature searchFeatureDBs(String str) { + IGVNamedFeature feat = FeatureDB.getFeature(str.toUpperCase().trim()); if (feat != null) { return feat; } else { @@ -575,7 +574,7 @@ public static class SearchResult { private String locus; private String message; - private NamedFeature feature; + private IGVNamedFeature feature; public SearchResult() { this(ResultType.ERROR, null, -1, -1); @@ -589,7 +588,7 @@ public SearchResult(ResultType type, String chr, int start, int end) { this.locus = Locus.getFormattedLocusString(chr, start, end); } - public SearchResult(NamedFeature feature) { + public SearchResult(IGVNamedFeature feature) { this(ResultType.FEATURE, feature.getChr(), feature.getStart(), feature.getEnd()); this.feature = feature; this.locus = Locus.getFormattedLocusString(chr, start, end); @@ -655,21 +654,21 @@ public int getEnd() { //May be null @ForTesting - public NamedFeature getFeature() { + public IGVNamedFeature getFeature() { return feature; } } /** * Get a list of search results from the provided objects, - * which must be NamedFeature objects. + * which must be IGVNamedFeature objects. * * @param objects * @return */ - public static List getResults(List objects) { + public static List getResults(List objects) { List results = new ArrayList(objects.size()); - for (NamedFeature f : objects) { + for (IGVNamedFeature f : objects) { results.add(new SearchCommand.SearchResult(f)); } return results; diff --git a/src/main/java/org/broad/igv/ui/commandbar/IGVCommandBar.java b/src/main/java/org/broad/igv/ui/commandbar/IGVCommandBar.java index 50179d4ff0..5655357e66 100644 --- a/src/main/java/org/broad/igv/ui/commandbar/IGVCommandBar.java +++ b/src/main/java/org/broad/igv/ui/commandbar/IGVCommandBar.java @@ -280,10 +280,9 @@ private void roiToggleButtonActionPerformed(java.awt.event.ActionEvent evt) { // - public void receiveEvent(Object e) { + public void receiveEvent(IGVEvent e) { - if (e instanceof ViewChange) { - ViewChange event = (ViewChange) e; + if (e instanceof ViewChange event) { if (event.type == ViewChange.Type.ChromosomeChange || event.type == ViewChange.Type.LocusChange) { String chrName = FrameManager.getDefaultFrame().getChrName(); roiToggleButton.setEnabled(!Globals.CHR_ALL.equals(chrName)); @@ -294,10 +293,9 @@ public void receiveEvent(Object e) { } updateCurrentCoordinates(); - repaint(); // TODO Is this neccessary? - } else if (e instanceof GenomeChangeEvent) { - GenomeChangeEvent event = (GenomeChangeEvent) e; - Genome genome = event.genome; + repaint(); // TODO Is this necessary? + } else if (e instanceof GenomeChangeEvent event) { + Genome genome = event.genome(); refreshGenomeListComboBox(); if(genome.getShowWholeGenomeView()) { chromosomeComboBox.setVisible(true); diff --git a/src/main/java/org/broad/igv/ui/commandbar/SearchTextField.java b/src/main/java/org/broad/igv/ui/commandbar/SearchTextField.java index 6db6d66098..bf50368aca 100644 --- a/src/main/java/org/broad/igv/ui/commandbar/SearchTextField.java +++ b/src/main/java/org/broad/igv/ui/commandbar/SearchTextField.java @@ -2,20 +2,14 @@ import com.jidesoft.hints.ListDataIntelliHints; import org.broad.igv.feature.FeatureDB; -import org.broad.igv.feature.NamedFeature; +import org.broad.igv.feature.IGVNamedFeature; import org.broad.igv.logging.LogManager; import org.broad.igv.logging.Logger; -import org.broad.igv.ui.GlobalKeyDispatcher; -import org.broad.igv.ui.IGV; import org.broad.igv.ui.action.SearchCommand; import org.broad.igv.ui.panel.FrameManager; import javax.swing.*; import javax.swing.text.JTextComponent; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import java.util.List; /** @@ -63,8 +57,8 @@ public boolean updateHints(Object context) { return false; } else { //TODO Uncomment to use comprehensive feature search, note that it should support partial matches - //List features = SearchCommand.comprehensiveFeatureSearch(text); - List features = FeatureDB.getFeaturesList(text, SearchCommand.SEARCH_LIMIT); + //List features = SearchCommand.comprehensiveFeatureSearch(text); + List features = FeatureDB.getFeaturesList(text, SearchCommand.SEARCH_LIMIT); final List results = SearchCommand.getResults(features); Object[] list = SearchCommand.getSelectionList(results, false); if (list.length >= 1) { diff --git a/src/main/java/org/broad/igv/ui/dnd/GhostDropEvent.java b/src/main/java/org/broad/igv/ui/dnd/GhostDropEvent.java index 91bda7b6b8..ad2c445398 100644 --- a/src/main/java/org/broad/igv/ui/dnd/GhostDropEvent.java +++ b/src/main/java/org/broad/igv/ui/dnd/GhostDropEvent.java @@ -34,18 +34,18 @@ public class GhostDropEvent { - private Point point; - private Point startPoint; + private final Point point; + private final Point startPoint; List tracks; List sourcePanels; boolean tracksDropped = false; - public GhostDropEvent(Point startPoint, Point point, java.util.List tracks) { + public GhostDropEvent(Point startPoint, Point point, List tracks) { this.startPoint = startPoint; this.point = point; this.tracks = tracks; - sourcePanels = new ArrayList(); + sourcePanels = new ArrayList<>(); } public void addSourcePanel(TrackPanel panel) { diff --git a/src/main/java/org/broad/igv/ui/panel/CytobandPanel.java b/src/main/java/org/broad/igv/ui/panel/CytobandPanel.java index 61b8b4053c..cb1f1d2e4e 100644 --- a/src/main/java/org/broad/igv/ui/panel/CytobandPanel.java +++ b/src/main/java/org/broad/igv/ui/panel/CytobandPanel.java @@ -167,8 +167,7 @@ public void igvMouseClicked(MouseEvent e) { referenceFrame.centerOnLocation(newLocation); } - ViewChange result = ViewChange.LocusChangeResult(referenceFrame.chrName, referenceFrame.origin, referenceFrame.getEnd()); - result.setRecordHistory(true); + ViewChange result = ViewChange.LocusChangeResult(referenceFrame.chrName, referenceFrame.origin, referenceFrame.getEnd(), true); IGVEventBus.getInstance().post(result); } finally { @@ -197,8 +196,7 @@ public void mouseReleased(MouseEvent e) { } finally { WaitCursorManager.removeWaitCursor(token); } - ViewChange result = ViewChange.LocusChangeResult(referenceFrame.chrName, referenceFrame.origin, referenceFrame.getEnd()); - result.setRecordHistory(true); + ViewChange result = ViewChange.LocusChangeResult(referenceFrame.chrName, referenceFrame.origin, referenceFrame.getEnd(), true); IGVEventBus.getInstance().post(result); } isDragging = false; diff --git a/src/main/java/org/broad/igv/ui/panel/DataPanel.java b/src/main/java/org/broad/igv/ui/panel/DataPanel.java index bce45fc33b..028344d4d8 100644 --- a/src/main/java/org/broad/igv/ui/panel/DataPanel.java +++ b/src/main/java/org/broad/igv/ui/panel/DataPanel.java @@ -34,6 +34,7 @@ package org.broad.igv.ui.panel; import com.google.common.base.Objects; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.DataLoadedEvent; @@ -62,10 +63,6 @@ import java.text.DecimalFormat; import java.util.List; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; /** * The batch panel for displaying tracks and data. A DataPanel is always associated with a ReferenceFrame. Normally @@ -100,9 +97,9 @@ public DataPanel(ReferenceFrame frame, DataPanelContainer parent) { } @Override - public void receiveEvent(Object event) { - if (event instanceof DataLoadedEvent) { - if (((DataLoadedEvent) event).referenceFrame == frame) { + public void receiveEvent(IGVEvent event) { + if (event instanceof DataLoadedEvent e) { + if (e.referenceFrame() == frame) { log.debug("Data loaded repaint " + frame); repaint(); } diff --git a/src/main/java/org/broad/igv/ui/panel/FrameManager.java b/src/main/java/org/broad/igv/ui/panel/FrameManager.java index df20359821..b0fa3ad7f5 100644 --- a/src/main/java/org/broad/igv/ui/panel/FrameManager.java +++ b/src/main/java/org/broad/igv/ui/panel/FrameManager.java @@ -25,8 +25,9 @@ package org.broad.igv.ui.panel; -import com.jidesoft.utils.SortedList; +import htsjdk.samtools.util.Locatable; import org.broad.igv.event.GenomeChangeEvent; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.feature.Locus; @@ -43,8 +44,16 @@ import org.broad.igv.ui.action.SearchCommand; import org.broad.igv.ui.util.MessageUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.broad.igv.sam.AlignmentTrack; +import org.broad.igv.sam.SortOption; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author jrobinso @@ -260,7 +269,6 @@ public static void removeFrame(ReferenceFrame frame) { } public static void sortFrames(final Track t) { - frames.sort(Comparator.comparingDouble((ReferenceFrame f) -> t.getRegionScore(f.getChromosome().getName(), (int) f.getOrigin(), @@ -356,28 +364,74 @@ public static void addFrames(List newLociStrings) { IGV.getInstance().resetFrames(); } + /* + * todo: this shoold be combined with addFrames since they do confusingly similar things + * This takes an arbitrary number of new locations to add as well as a single existing frame to keep + * (possibly that's ignored if there are multiple reference frames due to how loci are processed in createLociList()) + * It doesn't merge overlapping frames like addFrames does which it probably should. + */ - @Override - public void receiveEvent(Object event) { - if (event instanceof GenomeChangeEvent) { - Genome newGenome = ((GenomeChangeEvent) event).genome; - boolean force = true; - getDefaultFrame().setChromosomeName(newGenome.getHomeChromosome(), force); - } + /** + * Open new frames displaying the locations included in toIncludeInSplit + * @param frame this is used to set the width of the new frames to be consistent + * @param toIncludeInSplit new locations to open frames for + * @param selectedReadNames a set of read names which will be sorted to the top of the open frames + */ + public static void addNewLociToFrames(final ReferenceFrame frame, final List toIncludeInSplit, final Set selectedReadNames) { + final Stream newLoci = toIncludeInSplit.stream().map(locatable -> getLocusStringScaledToFrame(frame, locatable)); + final Stream existingFrames = getFrames().stream().map(ref -> { + String name = ref.getName(); + return Locus.fromString(name) != null ? name : ref.getFormattedLocusString(); + }); + + //Can't use FRAME_COMPARATOR because that looks at the existing frames statically and they don't all exist yet + final Comparator comparator = Comparator.comparing(Locus::fromString, SortOption.POSITION_COMPARATOR); + + final List loci = Stream.concat(newLoci, existingFrames) + .sorted(comparator) + .distinct() + .collect(Collectors.toList()); + String listName = String.join(" ", loci); + //Need to sort the frames by position + GeneList geneList = new GeneList(listName, loci); + geneList.sort(comparator); + IGV.getInstance().getSession().setCurrentGeneList(geneList); + IGV.getInstance().resetFrames(); + + /* + We want the sort to happen after the frame refresh / track loading begins. + This puts the sort onto the event thread so that it happens after loading has already started. + Since loading reads happens asynchronously on a different thread from the event thread, it is likely + that the loading won't be done by the time the sort fires. In that case the sort will be set as the + action to perform when the load is finished + See {@link AlignmentTrack#sortRows(SortOption, Double, String, boolean, Set)} + */ + AlignmentTrack.sortSelectedReadsToTheTop(selectedReadNames); } + private static String getLocusStringScaledToFrame(final ReferenceFrame frame, final Locatable alignment) { + int adjustedMateStart = alignment.getStart() - 1; - public static class ChangeEvent { - List frames; + // Generate a locus string for the alignment. Keep the window width (in base pairs) == to the current range + Range range = frame.getCurrentRange(); + int length = range.getLength(); + int start = Math.max(0, adjustedMateStart - length / 2); + int end = start + length; + return alignment.getContig() + ":" + start + "-" + end; + } - public ChangeEvent(List frames) { - this.frames = frames; - } - public List getFrames() { - return frames; + @Override + public void receiveEvent(IGVEvent event) { + if (event instanceof final GenomeChangeEvent e) { + Genome newGenome = e.genome(); + boolean force = true; + getDefaultFrame().setChromosomeName(newGenome.getHomeChromosome(), force); } } + + public record ChangeEvent(List frames) implements IGVEvent {} + } diff --git a/src/main/java/org/broad/igv/ui/panel/HeaderPanel.java b/src/main/java/org/broad/igv/ui/panel/HeaderPanel.java index 38efadd6c6..f9aa680844 100644 --- a/src/main/java/org/broad/igv/ui/panel/HeaderPanel.java +++ b/src/main/java/org/broad/igv/ui/panel/HeaderPanel.java @@ -35,6 +35,7 @@ package org.broad.igv.ui.panel; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.event.ViewChange; @@ -42,7 +43,6 @@ import org.broad.igv.track.TrackMenuUtils; import org.broad.igv.ui.IGV; import org.broad.igv.ui.util.IGVMouseInputAdapter; -import org.broad.igv.ui.util.SwitchingLabelUI; import javax.swing.*; import java.awt.*; @@ -313,7 +313,7 @@ public int getSnapshotHeight(boolean batch) { } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if(label != null && frame != null) label.setText(frame.getFormattedLocusString()); } diff --git a/src/main/java/org/broad/igv/ui/panel/ReferenceFrame.java b/src/main/java/org/broad/igv/ui/panel/ReferenceFrame.java index b39556f130..871934f1ad 100644 --- a/src/main/java/org/broad/igv/ui/panel/ReferenceFrame.java +++ b/src/main/java/org/broad/igv/ui/panel/ReferenceFrame.java @@ -29,7 +29,6 @@ */ package org.broad.igv.ui.panel; -import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.ViewChange; @@ -38,10 +37,11 @@ import org.broad.igv.feature.Range; import org.broad.igv.feature.genome.Genome; import org.broad.igv.feature.genome.GenomeManager; +import org.broad.igv.logging.LogManager; +import org.broad.igv.logging.Logger; import org.broad.igv.prefs.Constants; import org.broad.igv.prefs.IGVPreferences; import org.broad.igv.prefs.PreferencesManager; -import org.broad.igv.sam.InsertionManager; import org.broad.igv.sam.InsertionMarker; import org.broad.igv.ui.IGV; import org.broad.igv.ui.util.MessageUtils; @@ -54,7 +54,7 @@ */ public class ReferenceFrame { - private static Logger log = LogManager.getLogger(ReferenceFrame.class); + private static final Logger log = LogManager.getLogger(ReferenceFrame.class); IGVEventBus eventBus; @@ -167,7 +167,7 @@ public void setVisible(boolean visible) { public void dragStopped() { setOrigin(Math.round(origin)); // Snap to gride - eventBus.post(ViewChange.LocusChangeResult(chrName, origin, getEnd())); + eventBus.post(ViewChange.LocusChangeResult(chrName, origin, getEnd(), false)); } public void changeGenome(Genome genome) { @@ -177,8 +177,7 @@ public void changeGenome(Genome genome) { public void changeChromosome(String chrName, boolean recordHistory) { boolean changed = setChromosomeName(chrName, false); // if (changed) { - ViewChange resultEvent = ViewChange.ChromosomeChangeResult(chrName); - resultEvent.setRecordHistory(recordHistory); + ViewChange resultEvent = ViewChange.ChromosomeChangeResult(chrName, recordHistory); eventBus.post(resultEvent); changeZoom(0); // } @@ -186,8 +185,7 @@ public void changeChromosome(String chrName, boolean recordHistory) { public void changeZoom(int newZoom) { doSetZoom(newZoom); - ViewChange result = ViewChange.LocusChangeResult(chrName, origin, getEnd()); - result.setRecordHistory(false); + ViewChange result = ViewChange.LocusChangeResult(chrName, origin, getEnd(), false); eventBus.post(result); } @@ -382,7 +380,7 @@ public void shiftOriginPixels(int delta) { double shiftBP = delta * getScale(); setOrigin(shiftBP + origin); - eventBus.post(ViewChange.LocusChangeResult(chrName, origin, getEnd())); + eventBus.post(ViewChange.LocusChangeResult(chrName, origin, getEnd(), false)); } public void centerOnLocation(String chr, double chrLocation) { @@ -395,7 +393,7 @@ public void centerOnLocation(String chr, double chrLocation) { public void centerOnLocation(double chrLocation) { double windowWidth = (widthInPixels * getScale()) / 2; setOrigin(Math.round(chrLocation - windowWidth)); - eventBus.post(ViewChange.LocusChangeResult(chrName, origin, chrLocation + windowWidth)); + eventBus.post(ViewChange.LocusChangeResult(chrName, origin, chrLocation + windowWidth, false)); } public boolean windowAtEnd() { @@ -448,7 +446,7 @@ public void jumpTo(Locus locus) { log.debug("Scale = " + scale); } - eventBus.post(ViewChange.LocusChangeResult(chrName, start, end)); + eventBus.post(ViewChange.LocusChangeResult(chrName, start, end, false)); } public double getOrigin() { @@ -713,7 +711,7 @@ private void beforeScaleZoom(Locus locus) { } /** - * Calculate the zoom level given start/end in bp. + * Calculate the minimum zoom level which can completely contain the given start/end in bp. * Doesn't change anything * * @param start @@ -722,10 +720,14 @@ private void beforeScaleZoom(Locus locus) { */ public int calculateZoom(double start, double end) { final double windowLength = Math.min(end - start, getChromosomeLength()); - return (int) Math.round(Globals.log2((getChromosomeLength() / windowLength) * (((double) widthInPixels) / binsPerTile))); + final double exactZoom = Globals.log2((getChromosomeLength() / windowLength) * (((double) widthInPixels) / binsPerTile)); + //round up so that you get a zoom level which contains the given window + return (int) Math.ceil(exactZoom); } + + private static int getChromosomeLength(String chrName) { Genome genome = getGenome(); diff --git a/src/main/java/org/broad/igv/ui/panel/RegionNavigatorDialog.java b/src/main/java/org/broad/igv/ui/panel/RegionNavigatorDialog.java index da7bc819df..772040615d 100644 --- a/src/main/java/org/broad/igv/ui/panel/RegionNavigatorDialog.java +++ b/src/main/java/org/broad/igv/ui/panel/RegionNavigatorDialog.java @@ -26,6 +26,7 @@ package org.broad.igv.ui.panel; +import org.broad.igv.event.IGVEvent; import org.broad.igv.logging.*; import org.broad.igv.feature.Range; import org.broad.igv.feature.RegionOfInterest; @@ -133,7 +134,7 @@ public void update(Observable observable, Object object) { synchRegions(); } - public void receiveEvent(Object e) { + public void receiveEvent(IGVEvent e) { synchRegions(); } diff --git a/src/main/java/org/broad/igv/ui/panel/TrackPanelComponent.java b/src/main/java/org/broad/igv/ui/panel/TrackPanelComponent.java index be7dcad47e..c92496b7f5 100644 --- a/src/main/java/org/broad/igv/ui/panel/TrackPanelComponent.java +++ b/src/main/java/org/broad/igv/ui/panel/TrackPanelComponent.java @@ -53,21 +53,15 @@ */ abstract public class TrackPanelComponent extends JPanel { - private static Logger log = LogManager.getLogger(TrackPanelComponent.class); + private static final String DELETE_TRACKS_KEY = "deleteTracks"; List mouseRegions; private TrackPanel trackPanel; - /** - * A scheduler is used to distinguish a click from a double click. - */ - protected ClickTaskScheduler clickScheduler = new ClickTaskScheduler(); - - public TrackPanelComponent(TrackPanel trackPanel) { this.trackPanel = trackPanel; setFocusable(true); - mouseRegions = new ArrayList(); + mouseRegions = new ArrayList<>(); initKeyDispatcher(); } @@ -80,15 +74,13 @@ public void actionPerformed(ActionEvent e) { } }; + final KeyStroke delKey = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0, false); + final KeyStroke backspaceKey = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0, false); - if (Globals.isDevelopment()) { - final KeyStroke delKey = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0, false); - final KeyStroke backspaceKey = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0, false); + getInputMap().put(delKey, DELETE_TRACKS_KEY); + getInputMap().put(backspaceKey, DELETE_TRACKS_KEY); + getActionMap().put(DELETE_TRACKS_KEY, delTracksAction); - getInputMap().put(delKey, "deleteTracks"); - getInputMap().put(backspaceKey, "deleteTracks"); - getActionMap().put("deleteTracks", delTracksAction); - } } public TrackPanel getTrackPanel() { @@ -186,7 +178,7 @@ protected void openPopupMenu(TrackClickEvent te, List extraItems) { MouseEvent e = te.getMouseEvent(); final Collection selectedTracks = getSelectedTracks(); - if (selectedTracks.size() == 0) { + if (selectedTracks.isEmpty()) { return; } diff --git a/src/main/java/org/broad/igv/ui/panel/ZoomSliderPanel.java b/src/main/java/org/broad/igv/ui/panel/ZoomSliderPanel.java index c78082b958..169c8e69b0 100644 --- a/src/main/java/org/broad/igv/ui/panel/ZoomSliderPanel.java +++ b/src/main/java/org/broad/igv/ui/panel/ZoomSliderPanel.java @@ -214,7 +214,7 @@ private void paintVisibilityThresholds(final Graphics2D transGraphics) { .sorted() .distinct() .map(threshold -> this.getReferenceFrame().calculateZoom(0, threshold)) - .collect(Collectors.toList()); + .toList(); transGraphics.setColor(TRANSPARENT_BLUE); Rectangle maxZoom = zoomLevelRects[zoomLevelRects.length - 1]; diff --git a/src/main/java/org/broad/igv/ui/supdiagram/AlignmentArrow.java b/src/main/java/org/broad/igv/ui/supdiagram/AlignmentArrow.java new file mode 100644 index 0000000000..7502b059df --- /dev/null +++ b/src/main/java/org/broad/igv/ui/supdiagram/AlignmentArrow.java @@ -0,0 +1,44 @@ +package org.broad.igv.ui.supdiagram; + +import org.broad.igv.feature.Strand; + +import java.awt.*; +import java.awt.geom.Point2D; + +/** + * An arrow shape that represents a single Supplementary Read in the supplementary alignment diagram + */ +public class AlignmentArrow extends Polygon { + /** The width of the arrow tip in pixels */ + public static final int ARROW_PX_WIDTH = 5; + final Strand strand; + + public AlignmentArrow(int midline, int height, int left, int right, Strand strand) { + super(); + final int floor = (midline - height); + final int startAdjusted = left - (strand == Strand.NEGATIVE ? ARROW_PX_WIDTH : 0); + final int endAdjusted = right + (strand == Strand.POSITIVE ? ARROW_PX_WIDTH : 0); + /* + 1 2 + 0 <|=======|> 3 + 5 4 + */ + final int[] xPoly = {startAdjusted, left, right, endAdjusted, right, left}; + final int[] yPoly = {floor + height / 2, floor, floor, floor + height / 2, floor + height, floor + height}; + this.xpoints = xPoly; + this.ypoints = yPoly; + this.npoints = xPoly.length; + this.strand = strand; + invalidate(); + } + + public Point2D getTip() { + int x = strand == Strand.NEGATIVE ? xpoints[0] : xpoints[3]; + return new Point2D.Double(x, ypoints[0]); + } + + public Point2D getTail() { + int x = strand == Strand.NEGATIVE ? xpoints[3] : xpoints[0]; + return new Point2D.Double(x, ypoints[0]); + } +} diff --git a/src/main/java/org/broad/igv/ui/supdiagram/SupplementalAlignmentDiagram.java b/src/main/java/org/broad/igv/ui/supdiagram/SupplementalAlignmentDiagram.java new file mode 100644 index 0000000000..a8df5d0b06 --- /dev/null +++ b/src/main/java/org/broad/igv/ui/supdiagram/SupplementalAlignmentDiagram.java @@ -0,0 +1,460 @@ +package org.broad.igv.ui.supdiagram; + +import htsjdk.samtools.util.Interval; +import htsjdk.samtools.util.Locatable; +import org.broad.igv.logging.LogManager; +import org.broad.igv.logging.Logger; +import org.broad.igv.sam.AlignmentTrack; +import org.broad.igv.sam.SupplementaryAlignment; +import org.broad.igv.sam.SupplementaryGroup; +import org.broad.igv.ui.IGV; +import org.broad.igv.ui.panel.FrameManager; +import org.broad.igv.ui.util.IGVMouseInputAdapter; +import org.broad.igv.util.ChromosomeColors; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +class SupplementalAlignmentDiagram extends JPanel { + + public static final int LABEL_GAP = 2; + private static final Logger log = LogManager.getLogger(SupplementalAlignmentDiagram.class); + + public static final int BORDER_GAP = 30; + public static final int BETWEEN_ALIGNMENT_GAP = 15; + public static final int BETWEEN_CONTIG_GAP = 10; + public static final int ALIGNMENT_HEIGHT = 10; + public static final Color SELECTED_COLOR = Color.BLUE; + public static final Color PRIMARY_BORDER_COLOR = Color.DARK_GRAY; + public static final int DEFAULT_WIDTH = 500; + + private Rectangle2D chrDiagramBounds = null; + private Rectangle2D readDiagramBounds = null; + + //This keeps track of the polygons on screen so it's possible to do bounds checks for mouse interaction + private final Map elementsOnScreen = new LinkedHashMap<>(); + private final Set selected = new LinkedHashSet<>(); + private final SupplementaryGroup toDraw; + + public SupplementalAlignmentDiagram(final SupplementaryGroup toDraw) { + this.toDraw = toDraw; + this.setBackground(Color.WHITE); + this.addMouseMotionListener(new IGVMouseInputAdapter() { + + @Override + public void mouseMoved(final MouseEvent e) { + selected.clear(); + //dumb brute force search but there should only ever be a handful of these so it should be fine + for (AlignmentArrow arrow : elementsOnScreen.keySet()) { + if (arrow.contains(e.getPoint())) { + selected.add(elementsOnScreen.get(arrow)); + repaint(); + return; + } + } + repaint(); + } + }); + + this.addMouseListener(new IGVMouseInputAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + IGV igv = null; // for testing we may not have an IGV instance available + try { + igv = IGV.getInstance(); + } catch (RuntimeException ex) { + log.info("Clicked " + selected.stream().map(SupplementaryAlignment::toString) + .collect(Collectors.joining("\n"))); + } + + if (igv != null && !selected.isEmpty()) { + final SupplementaryAlignment first = selected.stream().findFirst().get(); + final Set selectedReadNameSet = Set.of(toDraw.getReadName()); + + if (e.isShiftDown()) { //if shift is held add to the existing set of frames instead of replacing them + FrameManager.addNewLociToFrames(FrameManager.getDefaultFrame(), java.util.List.of(first), selectedReadNameSet); + } else { + igv.setDefaultFrame(first.getContig() + ":" + first.getStart() + "-" + first.getEnd()); + AlignmentTrack.sortSelectedReadsToTheTop(selectedReadNameSet); + } + igv.getAlignmentTracks().forEach( + t -> t.setSelectedAlignment(toDraw.unwrap()) + ); + } + } + } + }); + } + + + @Override + protected void paintComponent(final Graphics g) { + super.paintComponent(g); + setBackground(Color.WHITE); + ((Graphics2D) g).setComposite(getAlphaComposite()); + g.setColor(Color.LIGHT_GRAY); + elementsOnScreen.clear(); + final int halfHeight = getHeight() / 2; + final int width = getWidth(); + chrDiagramBounds = new Rectangle2D.Float(0, 10, width, halfHeight - 10); + drawContigOrder(g, chrDiagramBounds); + readDiagramBounds = new Rectangle2D.Float(0, halfHeight, width, halfHeight); + drawReadOrder(g, readDiagramBounds); + + if (!selected.isEmpty()) { + final SupplementaryAlignment first = this.selected.iterator().next(); + g.setColor(Color.BLACK); + g.drawString(String.format("%s:%d-%d", first.getContig(), first.getStart(), first.getEnd()), 30, getHeight() - g.getFontMetrics().getHeight() + 2); + } + } + + private void drawReadOrder(final Graphics g, final Rectangle2D bounds) { + g.setColor(Color.DARK_GRAY); + ((Graphics2D)g).draw(bounds); + g.drawString("Read Order", (int)bounds.getX()+5, (int)bounds.getY()+15); + Map saInReadOrder = drawAlignmentsInReadOrder((Graphics2D) g.create(), toDraw, selected, bounds); + drawArcs((Graphics2D) g.create(), toDraw, selected, saInReadOrder, bounds); + final int lowestArrowPoint = saInReadOrder.values().stream().mapToInt(a -> (int) a.getBounds2D().getMaxY()).max().getAsInt(); + drawReadLengthLabel(((Graphics2D) g.create()), lowestArrowPoint + 15 , saInReadOrder); + saInReadOrder.forEach((k, v) -> elementsOnScreen.put(v, k)); + g.setColor(Color.DARK_GRAY); + ((Graphics2D)g).draw(bounds); + } + + private void drawContigOrder(final Graphics g, final Rectangle2D bounds) { + g.setColor(Color.DARK_GRAY); + ((Graphics2D)g).draw(bounds); + g.drawString("Alignment Order", (int)bounds.getX()+5, (int)bounds.getY()+15); + Map saInPositionOrder = drawAlignmentsInCondensedChromosomeOrder((Graphics2D) g.create(), toDraw, selected, bounds); + drawArcs((Graphics2D) g.create(), toDraw, selected, saInPositionOrder, bounds); + final int lowestArrowPoint = saInPositionOrder.values().stream().mapToInt(a -> (int) a.getBounds2D().getMaxY()).max().getAsInt(); + drawContigLabels((Graphics2D) g.create(), lowestArrowPoint + 15, saInPositionOrder); + saInPositionOrder.forEach((k, v) -> elementsOnScreen.put(v, k)); + g.setColor(Color.DARK_GRAY); + ((Graphics2D)g).draw(bounds); + } + + private void drawReadLengthLabel(final Graphics2D g, final int mid, final Map saInReadOrder) { + java.util.List arrowsInOrder = new ArrayList<>(saInReadOrder.values()); + int left = (int)arrowsInOrder.get(0).getBounds().getMinX(); + int right = (int)arrowsInOrder.get(arrowsInOrder.size() - 1).getBounds().getMaxX(); + final int baseCount = toDraw.getBaseCount(); + g.setColor(Color.BLACK); + drawCenteredStringWithRangeLines(g, mid, "Length in bases = " + baseCount, left, right); + } + + private static void drawArcs(final Graphics2D g, + final SupplementaryGroup toDraw, + final Set selected, + final Map saToArrowMap, + final Rectangle2D bounds) { + toDraw.iterateInReadOrder() + .forEachRemaining(sa -> { + SupplementaryAlignment next = toDraw.getNextInRead(sa); + if (next != null) { + final boolean highlight = selected.contains(sa) || selected.contains(next); + drawArc(g, saToArrowMap.get(sa), saToArrowMap.get(next), highlight, bounds); + } + }); + } + + private static Composite getAlphaComposite() { + final float alpha = 0.75f; + final int type = AlphaComposite.SRC_OVER; + return AlphaComposite.getInstance(type, alpha); + } + + private static Map drawAlignmentsInReadOrder(final Graphics2D g, final SupplementaryGroup toDraw, + final Set selected, Rectangle2D bounds) { + int midline =(int) (bounds.getY() + .5 * bounds.getHeight()); + final Map positions = new LinkedHashMap<>(); + final int totalAlignedBases = toDraw.getBaseCount(); + final int scaledAlignmentGap = scale(2, BETWEEN_ALIGNMENT_GAP, bounds.getWidth()); + final int scaledBorderGap = scale(BORDER_GAP / 3, BORDER_GAP, bounds.getWidth()); + final double availableSpace = bounds.getWidth() - (2 * scaledBorderGap + (toDraw.size() - 1) * scaledAlignmentGap); + + double lastPosition = scaledBorderGap; + for (SupplementaryAlignment sa : (Iterable) toDraw::iterateInReadOrder) { + final double spaceToUse = getSpaceToUse(availableSpace, sa.getNumberOfAlignedBases(), totalAlignedBases); + final int end = (int) (lastPosition + spaceToUse); + AlignmentArrow readArrow = new AlignmentArrow(midline, ALIGNMENT_HEIGHT, (int)( bounds.getX() + lastPosition) , (int)( bounds.getX() + end), sa.getStrand()); + lastPosition = end + scaledAlignmentGap; + positions.put(sa, readArrow); + } + + drawArrows(g, selected, positions, toDraw.getPrimaryAlignment()); + return positions; + } + + private static Graphics2D getSelectedGraphics(final Graphics2D g) { + Graphics2D selectedGraphics = (Graphics2D) g.create(); + selectedGraphics.setStroke(new BasicStroke(3.0f)); + selectedGraphics.setColor(SELECTED_COLOR); + return selectedGraphics; + } + + private static Map> groupBySpanningInterval(List intervals){ + List currentGroup = null; + Map> output = new LinkedHashMap<>(); + Locatable spanning = null; + if(intervals.isEmpty()){ + return Collections.emptyMap(); + } + for( T loc : intervals){ + if(currentGroup == null || currentGroup.isEmpty()){ + currentGroup = new ArrayList<>(); + currentGroup.add(loc); + spanning = loc; + } else if( spanning.overlaps(loc)) { + currentGroup.add(loc); + spanning = new Interval(spanning.getContig(), Math.min(spanning.getStart(), loc.getStart()), Math.max(spanning.getEnd(), loc.getEnd())); + } else { + output.put(spanning, currentGroup); + currentGroup = new ArrayList<>(); + currentGroup.add(loc); + spanning = loc; + } + } + output.put(spanning, currentGroup); + return output; + } + + //Draw the alignments in chromosome order but give an indication of how close they are to each other i.e. within a 1kb window or not + //Handle overlapping alignments + private static Map drawAlignmentsInCondensedChromosomeOrder(final Graphics2D g, final SupplementaryGroup toDraw, + final Set selected, Rectangle2D bounds) { + + + final double midline = bounds.getY() + .5 * bounds.getHeight(); + final Map positions = new LinkedHashMap<>(); + final List contigs = toDraw.getContigs(); + final int scaledAlignmentGap = scale(2, BETWEEN_ALIGNMENT_GAP, bounds.getWidth()); + final int scaledContigGap = scale(2, BETWEEN_CONTIG_GAP, bounds.getWidth()); + final int scaledBorderGap = scale(BORDER_GAP / 3, BORDER_GAP, bounds.getWidth()); + + final var groupedBySpan = groupBySpanningInterval(toDraw.streamInPositionOrder().toList()); + final Map> byContig = groupedBySpan.keySet() + .stream() + .collect(Collectors.groupingBy(Locatable::getContig, LinkedHashMap::new, Collectors.toList())); + + + //tihs should probably vary per contig instead of being uniform + final double perContigAvailableSpace = (bounds.getWidth() - (2 * scaledBorderGap + (contigs.size() -1) * scaledContigGap))/((double)contigs.size()); + + int contigStart = scaledBorderGap; + for(var contigEntry: byContig.entrySet()){ + //find the available space for each contig and set the drawing head there + int contigEnd = (int)(contigStart + perContigAvailableSpace); + List distinctSpans = contigEntry.getValue(); + //find the reference length of all the span groups on this contig + int totalSpansLength = distinctSpans.stream().mapToInt(Locatable::getLengthOnReference).sum(); + int spanStart = contigStart; + for(Locatable span: distinctSpans){ + //now handle each span group + int spanLength = span.getLengthOnReference(); + int spanSpaceAvailable = (int)getSpaceToUse((double) perContigAvailableSpace - (distinctSpans.size() -1) * scaledAlignmentGap, spanLength, totalSpansLength ); + final List activeSpanGroup = groupedBySpan.get(span); + for(int i = 0; i < activeSpanGroup.size(); i++){ + //each read in the span is placed relatively within the space + SupplementaryAlignment sa = activeSpanGroup.get(i); + int scaledReadStart = (int)getSpaceToUse(spanSpaceAvailable, sa.getStart() - span.getStart(), spanLength); + int scaledReadEnd = (int)getSpaceToUse(spanSpaceAvailable, sa.getEnd() - span.getStart(), spanLength); + // height offset is used to try to layout overlapping arrows above instead of ontop of each other + int heightOffset = ALIGNMENT_HEIGHT - (int)((2*ALIGNMENT_HEIGHT) * (1.0/(activeSpanGroup.size()+1.0))*(i+1.0)); + final AlignmentArrow readArrow = new AlignmentArrow((int)midline + 2*heightOffset, ALIGNMENT_HEIGHT, + (int)bounds.getX() + spanStart + scaledReadStart, + (int)bounds.getX() + spanStart + scaledReadEnd, + sa.getStrand()); + + positions.put(sa, readArrow); + + } + spanStart += spanSpaceAvailable + scaledAlignmentGap; + } + //move contig start forward + contigStart = contigEnd + scaledContigGap; + } + + // | ... [ ]>-5kbp<[ ] ... [ ]> | |[ |> ... | + // Contigs all the same scale? + // Contigs proportional to actual size? < + //1 divide available space into contig zones + //2 go through all reads on contig and count necessary zones. + // edges if the read hits left / right edge + // merge overlappers into single zones + // discover close by / far away zones + + drawArrows(g, selected, positions, toDraw.getPrimaryAlignment()); + return positions; + } + + //This draws the aligments scaled according to their aligned length on the reference and in chromosome order + //TODO fix overlapping alignments + private static Map drawAlignmentsInPositionOrder(final Graphics2D g, final SupplementaryGroup toDraw, + final Set selected, final int width, final int midline) { + final Map positions = new LinkedHashMap<>(); + final List contigs = toDraw.getContigs(); + final int totalAlignedBases = toDraw.getLengthOnReference(); + final int scaledAlignmentGap = scale(2, BETWEEN_ALIGNMENT_GAP, width); + final int scaledContigGap = scale(2, BETWEEN_CONTIG_GAP, width); + final int scaledBorderGap = scale(BORDER_GAP / 3, BORDER_GAP, width); + final double availableSpace = width - (2 * scaledBorderGap + (toDraw.size() - 1) * scaledAlignmentGap + (contigs.size() - 1) * scaledContigGap); + + String lastContig = contigs.get(0); + double lastPosition = scaledBorderGap; + for (SupplementaryAlignment sa : (Iterable) toDraw::iterateInPositionOrder) { + + final double spaceToUse = getSpaceToUse(availableSpace, sa.getLengthOnReference(), totalAlignedBases); + final String newContig = sa.getContig(); + if (lastPosition != scaledBorderGap && !Objects.equals(lastContig, newContig)) { + lastPosition += scaledContigGap; + } + + lastContig = newContig; + final int end = (int) (lastPosition + spaceToUse); + + AlignmentArrow readArrow = new AlignmentArrow(midline, ALIGNMENT_HEIGHT, (int) lastPosition, end, sa.getStrand()); + positions.put(sa, readArrow); + lastPosition = end + scaledAlignmentGap; + } + + drawArrows(g, selected, positions, toDraw.getPrimaryAlignment()); + return positions; + } + + /** + * Scale a value between min/max based on the difference between width and DEFAULT_WIDTH + */ + private static int scale(final int min, final int max, final double width) { + if (width >= DEFAULT_WIDTH) { + return max; + } else { + final double scaleDown = width / (double) DEFAULT_WIDTH; + return Math.max((int) (max * scaleDown), min); + } + } + + private static void drawArrows(final Graphics2D g, final Set selected, final Map positions, final SupplementaryAlignment primary) { + //first draw background colors + positions.forEach((sa, readArrow) -> { + g.setColor(ChromosomeColors.getColor(sa.getContig())); + g.fill(readArrow); + }); + + //outline primary read + g.setColor(PRIMARY_BORDER_COLOR); + final AlignmentArrow primaryShape = positions.get(primary); + g.draw(primaryShape); + + //next draw borders on top to work in case of overlaps, this should come last so it doesn't get clobbered + //in the case of overlaps + for (Map.Entry pair : positions.entrySet()) { + SupplementaryAlignment sa = pair.getKey(); + AlignmentArrow readArrow = pair.getValue(); + g.setColor(ChromosomeColors.getColor(sa.getContig())); + Graphics2D g2 = selected.contains(sa) ? getSelectedGraphics(g) : g; + g2.draw(readArrow); + } + } + + private static double getSpaceToUse(final double availableSpace, final int numberOfBasePairs, final int totalAlignedBases) { + final double fractionOfWhole = (double) numberOfBasePairs / totalAlignedBases; + return fractionOfWhole * availableSpace; + } + + private static void drawContigLabels(final Graphics2D g, final int mid, final Map positions) { + positions.keySet(). + stream() + .collect(Collectors.groupingBy(SupplementaryAlignment::getContig)) + .forEach((contig, alignments) -> { + int start = (int) positions.get(alignments.get(0)).getBounds().getX(); + final Rectangle rightmostBounds = positions.get(alignments.get(alignments.size() - 1)).getBounds(); + int end = (int) (rightmostBounds.getX() + rightmostBounds.getWidth()); + g.setColor(ChromosomeColors.getColor(contig)); + drawCenteredStringWithRangeLines(g, mid, contig, start, end); + }); + } + + private static void drawCenteredStringWithRangeLines(final Graphics2D g, final int mid, final String string, final int start, final int end) { + final FontMetrics fontMetrics = g.getFontMetrics(); + final int labelWidth = fontMetrics.stringWidth(string); + // s----label-----e + final int lineY = mid; + final int height = fontMetrics.getHeight(); + if (labelWidth + 2 * LABEL_GAP < end - start) { + final int leftLineEnd = (start + end - labelWidth) / 2 - LABEL_GAP; + final int rightLineStart = (start + end + labelWidth) / 2 + LABEL_GAP; + g.drawLine(start, lineY - 2, start, lineY + 2); + g.drawLine(end, lineY - 2, end, lineY + 2); + g.drawLine(start, lineY, leftLineEnd, lineY); + g.drawString(string, leftLineEnd + LABEL_GAP, lineY + height / 3); + g.drawLine(rightLineStart, lineY, end, lineY); + } else { + g.drawString(string, start, lineY + height / 3); + } + } + + private static void drawArc(final Graphics2D g, final AlignmentArrow currentPos, final AlignmentArrow nextPos, final boolean highlight, Rectangle2D bounds) { + CubicCurve2D c = new CubicCurve2D.Double(); + Point2D from = currentPos.getTip(); + Point2D to = nextPos.getTail(); + g.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); + + + // Determine the distance between the two points + double distance = Math.abs(from.getX() - to.getX()); + + // Calculate the height proportional to the distance. We'll use 1/4 of the bounding box height + // as a baseline and then adjust based on the distance. + double minHeight = bounds.getY(); + double heightAdjustment =1; //.75 + .25 * (distance / bounds.getWidth()); + double maxHeight = bounds.getHeight() - 5; + double actualHeight = maxHeight * heightAdjustment; + double controlY = bounds.getY()+(bounds.getHeight() - actualHeight); + + // Create two control points for the Bezier curve. + Point2D control1 = new Point2D.Double(from.getX() + (to.getX() - from.getX()) / 4, controlY); + Point2D control2 = new Point2D.Double(from.getX() + 3 * (to.getX() - from.getX()) / 4, controlY); + + + c.setCurve(from, control1, control2, to); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if (highlight) { + g.setColor(SupplementaryAlignmentDiagramDialog.ARC_HIGHLIGHT_COLOR); + } else { + g.setColor(Color.GRAY); + } + g.draw(c); + drawPoint(g, to); + } + + + private static void drawPoint(Graphics2D g, Point2D point) { + g.draw(new Ellipse2D.Double(point.getX() - 1, point.getY() - 1, 2, 2)); + } + + private static void writeContigName(final Graphics g, final String contig, final double x, final int y) { + Color originalColor = g.getColor(); + try { + g.setColor(ChromosomeColors.getColor(contig)); + g.drawString(contig, (int) x, y); + } finally { + g.setColor(originalColor); + } + } +} diff --git a/src/main/java/org/broad/igv/ui/supdiagram/SupplementaryAlignmentDiagramDialog.java b/src/main/java/org/broad/igv/ui/supdiagram/SupplementaryAlignmentDiagramDialog.java new file mode 100644 index 0000000000..7304ed2250 --- /dev/null +++ b/src/main/java/org/broad/igv/ui/supdiagram/SupplementaryAlignmentDiagramDialog.java @@ -0,0 +1,79 @@ +/* + * 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.ui.supdiagram; + +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMRecordSetBuilder; +import htsjdk.samtools.SAMTag; +import org.broad.igv.sam.Alignment; +import org.broad.igv.sam.SAMAlignment; +import org.broad.igv.sam.SupplementaryGroup; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + + +public class SupplementaryAlignmentDiagramDialog extends JDialog { + + public static final Color ARC_HIGHLIGHT_COLOR = Color.CYAN; + + private final SupplementalAlignmentDiagram diagram; + + public SupplementaryAlignmentDiagramDialog(Frame frame, Alignment alignment, Dimension dimension) { + super(frame); + setSize(dimension); + diagram = new SupplementalAlignmentDiagram(new SupplementaryGroup(alignment)); + this.add(diagram); + + //todo make this only on top of the parent window, not all applications? + setAlwaysOnTop(true); + setTitle("SAs for " + alignment.getReadName()); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + diagram.setVisible(true); + } + + + //for quick component testing + public static void main(String[] args){ + final SAMRecordSetBuilder samRecords = new SAMRecordSetBuilder(); + samRecords.addPair("read1", 13, 50255292, 50255400 ); + final List records = new ArrayList<>(samRecords.getRecords()); + + final SAMRecord record = records.get(0); + record.setAlignmentStart(10000); + + String read = "m141213_163442_00118_c100750562550000001823151707081585_s1_p0/6849/0_5821\t2064\t14\t50255292\t38\t1302S12M1I5M1I25M1D12M1I7M1I2M1I10M1D4M1I7M1I10M1I6M1I4M1D9M5I4M1I4M2I18M1I9M1I5M1I20M1I13M1I6M1I6M1I16M1I3M1I13M1D9M1I4M2I4M1D14M1I21M1I1M1D5M11I2M1I11M1D10M1I12M2I6M1D3M1D11M1D3M1I3M1I19M1I3M2I6M1I8M1D1M2I1M1I6M1I9M3I2M1I4M1I1M1I10M1D1M1I9M1D6M1D4M2D6M2D7M1D13M1I5M1D6M1D8M1I3M4I3M2D1M1D9M1D25M1I4M1D20M1I18M1I9M1D3M1I2M1I4M1I6M1I5M1I18M1I4M3I17M1I1M1I9M1I11M1I24M1I5M1D14M3I16M1I23M2I14M1I6M1I10M1I10M1I13M2I15M1D10M1I12M1I2M1I6M1I11M1I5M1I4M1I11M1I6M1I9M1I19M1I1M1I29M1D23M1D16M1I23M1D3M1I2M1D9M1I20M2I4M1D19M1I11M1D21M1I5M1I11M1I2M1D2M1D7M1D16M1I4M1I8M1D9M1D18M1I12M1I9M1D5M1D5M1I5M1I5M1I36M1D7M3145S\t*\t0\t0\tATTCAAGGCACCAGCAGATTCTTGTCTGAATAGGGCTTGCCCCTCAAAGATTGGTACCTATAAGGCTGAGGTTGGGTGGCTCATACCTGTAATCCCAGCACTTTGGCGAGGCCAAGGTCGGGGGGATCCTTGGCCCAGGAGATTCGACACCAGCCTGCGGCAACAAGGGAGACCTCAATCTTAAAAAAAAGATGGCACCTTATTGCAGTGTACCTCACGTGGCAACAGGGGCACTCATCCTCATTCATGACACAGAGGCCCCTCGTTGACTTCACTTCCTAAAGGCCACTCTTAAATACCATCCACACTGGGCTATTTAGGTTCCCATATATGAATGTGGGGATAATTAACTGTAGAACCATAGCAGCCGTAACTCATGAAAGAACATTAATAAGTTTAACACCTCAGTGAGTATTTGTTGAGTCGTAGGATTAACAGAACATAAGGTTATTTGTAACAAATTTTTTAAAAAGACCACCAAAAATGGTTTTAGTCATTACTAAACCAACAGTGTCAGCAGGAAAAAAAGAAAATAGTAATTACAGAATTCCCGTAAGACATTACCTGCCATAAACTGGAGCACACAATTCATAACTCAAGCTTGACATAAAAAAATGCTGGTGGCAAAAGTAAAATATTTTTATTGCCTCTATTTTAGCCGTTATTAAAAACATAAACAGGCTGGTGACATGGTGTCTTACAGCCTGTATCCCAACACTTTGGGAGGCCAAGGGCAGGCGGAATTCACGGAGATCAGGACGATTTCAAGACCAGCACTGGCCAACAATGGTTGAACCCCAATTCACCACTTAAAATACCAAAATAAGCCAGGCGTAGATGGCGCATTTGCTTAGTAATCCCAAGCTACTCAGAAGGCTGAGGCGGATAATTGCTTGAATCACGGGAGGTGGCAGGTTGCCAGTGAGCTGAGATTGTGCCACTTGCACTCCAGCCTGGGACAAGAGCCGAGCGACTGCCTCAAAAAAAAAAAAAAAAAAAAAAAAATTCAAAGAGAGGTAAATTTCATGTAAGAAGATATACAGCAAGGCGTGGGCTTGGCTGTACTAGTTAACAAAACAATATGTGTGTAGGCGATGTGTCTGTCTTAGCTGGAAAATATTCGTCAGTGGTTTTGTAACCAGGGTACTTGGCTCTTTAAATGGTGTACTCGATGTTCAACATTATGTACGACCATATTGGTGTATAGATCGTGAGTGTGTTAGGAGTGTGGATGAAATTTATTTGTGGGGTGTCTTCAACAGACTGTTTCCAATTATTCTCTGTGTTGGTTTTTTGTGTGTGTTGAGATGGAGTACTCGCATCTGTCGACCAGGCTGGAGTGCAATGCACAATCTCGGACTCACTGACACAGCTCTGCCTCCAGAGTTCACGACTATTCTGCCTTCAGCCTTCCCAGTAGCTGAGGGGGGACTAACAGAGGCGCTCACCACCACACCCAGGCTAATTTCTTTTGTTATTTTTTTAGTAGAGACAGCGGTTTCACCACGTATAGCCAGGCATGGTTCTCAATTTCCTGACCTTTGTTGATCTGCCTGCCTGGCCTCTCAAAAGATTGCTGGATTACAGGCGTGAAGCCACCGTGCCAGGCCTCTCATTCTTATCTTTTTTTTTATATTAACTCAGTTCCATCAGAATCCAAAGCCTCTCCGCTTAAAATGATACTTCCTAAAGCTCTGTAAACCAAATTGTTATCAGCTAATCGAAGTACTAACCATTCTATTTGGCTTAGCATAAGATACGTGTGTGGATGTGGTAGTGTCTCGTGGTGTGTGTTGGTTGTATTGTGATGGGGGGGTGTCACTTGTCTTGTGTGGTGTACTGTGGTGCCAGTGTGGTTCCTGACTCCAGGAGGGTAATCACCCTAATTAAGAAGCAGGAAGCCACAAAGTAGCCATACCTTAGTCATCATGTGGATCATCTTACAGCAAATCTCTGTTAACTTCATAACGTTATAAACCTCAAGGAACGGAGTATTCTATTTTCTTAATGTTGTCAGAGGCACCCTCTGTCCCACCTCTAGGTTTTCTGGGGCTGTTTCTTCACAGGTTTGGTCTTTGTTTTTCCTTGGTTCTTCCCCTTCTTCCCTTTTTTCTTCTTTGTTTGAACCTGACCAGACTTTAATTCAGTAGAGGAAAACATCAGTCCAGTGTTGTATTTTTCAATACTGTGGGAATATTTTTACAAGATGTATTGAATTGCCTGTCATACGGCATAGGCAGGTGGGCTAGATGGCCCATTTTGAAAAAGCAAAAGAAAAAAATTGTTTTTAGAATGTTATTTACCCCCAGCATACCTTCATGATAAGTTCACGGTCTTCTTCATCTGGTCTTTGTATTTTTCTTTCATTTTTTCATTTGACTCTTACAAATTCAGAAGATTTTGGTTGAATATTTCAGCAGCAAAAAAAATGTCAACAAATACTTTTGCAAGAAATGGTGTTACTTTATTATTCTCTGTCAAAATGCTTACTGAATAAGGCAACCATATATAATAAACATATTGAGGCTACAAGTTGGATAACTGAAGGTCCTTCATCTTCAGGAATTTACATAGGGTGGAGAGAAGATTACACAGAGAAGTTAAGAATACTTAACAAACATTAAAGAAACTGTTGAGGAAGAAATAAAGTAAAACAGTATTGGAAAAGCAGTCTAATCCTCCTGCCTCAGCCTCCGAGTAGCTGGGGACTACAAGCGCTGCTGCCATGCCCGGCTAATTTTCTGTTATTTTTTAGTAGAGATGGGGTTATTCCACTGTATTAGCCAGGATGGTCTCCAATCTCCTGACTTGTGATTGCCCGCCTCGCCTCCCAAATTGCTGCGGATTTAACAGCGATGAGCCACCACTGCACCGGCCAAAAGTTTTTAAAAAATATAGTCTCAAGAAAGCATAACCATACTGTTAGTTGTGTTTAACTACTAAAAAATGAGGGGGTCCAAAATTTGGACTTCTGAGCAACTAAACAAAATAGAAACATGATCGTTTATATGGATCTTTCCAATGAGCCTATTAACTTTTCTCCTTCTTTAAATCCAATGGTTTAATTTTCAATATTTTCATTTATCAAGTGATAACATTAAGTAAACAAAAATTAAAATCACATTTTTTACCTTAGGTTGTGAGCTGATTGTGTCCTCCAAAAGATAGTGATTGAGGACAATCTCCACGTTTACCTATGAAGTGTGGCCTTATTTGGTTGATAGTGTCTTTCAGATGTAAGCAAGTTAAAATGATATTCTATACTGGATTAGGGTGGGCCCTTAAATCCAACAACTTCTGACATTATGAGAAAGGCATGTTGGGAAGAACATCGAGACAGAAATACACAATTGAAACATGAAATTGTTGACAGAGCAGGACTTGCCATGCTCACTGTAGCCAGGAAATGCTGAGGACCACCCGCAGCCACAGGAAGCTCCACAGAGGACAAGAAGAATTTTCCTCTACCAAGAACCTTCAAGAAAAGCCAGGTTCTTGTGGTGGGCGTGCTTTCATTCATACTTGCAGGTCTTCAAAACTGTGAGGCAATAATTTCTGTTGCTTTTAAAGCCACTCGGTTTGTGGTCATTTGCCACGCGCCTAGGAATCAAAACTAACTATCAACTGGCCAACTTTCGGTTGTTTTTATTTATTTTTTATTTTCATTTTTATTTTATTTTTTTATTTGTTTGAGATGGAGTCTCCCTTGTTGCCCCAGCTGGAGTGCAGAGTGGGCTATGATCCTCAAGCTCACTTGCAACCCCATGGGCCTCCTGGGTTCAAGTGATTCTCCTTGCTCAGCTCCTGAGTAGCTGGGATATACAGGCACACGCCACCATGCGCGGCTAAATTTTTTGTATTTGAGTAGGACGAGGGGTTCACCTGTTGGCCCAAGGCTTTGGATTCTCGAAACTCCTGACGTCAAAAGTGGATCCGCCCACCTTGGCTTCCAAAAGTGCTGGATTTACAGGCGAAAAGCCACTGCACCCAGCCTGTTTTTAATTAACACCATTTTGGTTTGAAATGCTAGAAGGAATTGGGACATTTAACATATACTTTCATTAGAGAGTAATTTTTTTATTGTAAGATATTAATTTTCTGTTTTTCTTTTTCATCATCTCTTTTTTCTAAAGTTTTCAATGTTGAGCTTGTATATCTTTTTGCAATAACAAAAACTTTCATTTAAAAACACTATTGTCACCATTATTTAACATTTTTCTCGTAAGTTTCTAGCGGTAAGTGGCTAAGAAATGGATGTCGGGTTTGATTTTAACAGCTCCCTGGGTCGGGGAGAACAGGGTTGGACTTGAAATCACAGGAAACATGAATCTTCAGTCCCAAGCTGTGCCATTTCCAGCTGTATGATCTTGAGTAAGCCACTGAACTTTTCGTAAACCTCAAGTTTCCCCCACCTCCCTATGATGTTGTTTCTATTGGGCTCTTCAATTTTCTACCACCGTGTGCCCTTTAAAGAAGGATGAGAGCAAGAGGGAAACAAATGTCAAACATTAATTGCATTTTTAATACAGTTCACTGGAGGATGACCCTGGGGAAGAGGGATTGAGGGAAATCAATTCTGGTGATGAAAACAATAATCGAGAATTACAAGAAGGGTCACAGCTTTAAATCTGGATTAGAGAGAAAAATGCCTCTGCATTTTTCTTTGCCTTTGTGGGGTTCAGTCAGACTGTTACTTGATTCAATGTTTACTGTGATTCAAACAGTTCTGAGGGGAAATGCTTTGTGTGTCATCAACTATTACAGACACTGAGGGGACAGAGATGTTCCAGACATAACCCTGTTTTCCTTCATGTGGGCTTCTCTTGTGAGTGGGTGAAGGGTGGGGTCAAAGTACTAGATGGATTTTTTTTAAATTTTTTATATTGTACTTTAAGTTTTAGGGGTACATGTGCACAAATGTACAGGTTTGTTACATATATTTACATGTGCCCATGTTTGGTGTGCTGCAACCATTAACTCGTCAGTTAACATTTAGGGTTATAATCTCCTAATGCTATTCTACCCCGCTCCCGCACCCATAACAGGCTTCTGTTATTGATGTTTCCCTTTTCTGTGTCCATGTGTTTCTCATTGTTCAATTCCACCTGTTGAGTGAGACATGCCAGTGTTTTTGGTTTTTTGTCCTTGTTTGATGTTTGCGGAGAATGAATGGTTTTCCAGCTTCATCCATGTCCCTACAAAGACATGAAAATTCATTTAAGATGGATTTTTTTAAATGACAAAGCAGAATCAGAAAAAATGGCTAAGAGAGGATGAATTGGCTGAGCCTTCTTTTCCATGCATTGGTGTCACCTTTATAAAGGATTGTGAAAATGTCAAGGCAGGGCAGCTGATCTTTTATTTATTGGTTTTCATTACAGGTGGACCTGGCCAGAAGCCCTTGGGAAACTGGCGGTGCGCTACGGGTAATTTATTTCTAGGCACCTTCAAAGGAGAAAAGGGCAGTGTCCTCCCTTCTCAACTGGATATTGTGCTAGACCATAGTTGCATACCGCCCACAAGCCCCCAAGCATCAGCTGGCCTGGCATCCCTGCCCAGAAGGGCCCATTGCCACTGGTCTTTCCACAAAAACCTGAGCCTTAGGCCAAATTAAAAAGCCACTTGTAGGCATTTTTACCACTTGAATTTACAGCCAAGAGGAAAATCCAACCATGGGGAAAAAGATCCACTGGCTTTTGGCAATTTTCCAAACTTTAAAAACCCCCAAAAGGGCCAAAAAAGGCAAAAACTACCCCAAAGGCCAAAAAGGCTATTAGGAACATTACAAGGAAAAAAAGTCTTTGATATTTCTTTTCTTTGT\t*\tSA:Z:17,64042266,-,2676S17M1D10M1I14M1D26M1I2M1I20M2I1M1I23M1I11M1D8M1D10M1D16M1I3M1I3M1D1M1I4M1I11M1I2M1I6M1D10M1I11M1D3M1D11M1D8M3I2M1I14M1I7M4I12M1I3M1I9M1D13M1D23M1I4M1I13M1D12M1I38M1I11M1I5M1D11M1I17M1I14M1D12M1D8M1I2M1I3M1D12M1I2M2I8M1I19M1D1M2D11M1D25M1I2M1I21M1I28M1I4M1D4M1I1M2I4M1I22M1I5M1I3M1D3M1I9M2I6M1I9M1D2M1D4M1D11M1D2M1I18M1D2M1I13M1I23M1I17M1I7M1I2M1D4M1I21M1I25M1D12M1I3M1I29M1D2M1D1M1D6M1D8M1I8M1D17M1D13M1I54M1D5M1I4M1D12M2I1M1I3M1I5M1I3M1I7M1I9M1I1M2I25M1I2M1D5M1D18M1I27M1I2M1I8M1D9M1D3M1I11M1D6M1I2M1I4M2I3M2I5M1I13M2I4M1I30M1D3M1I9M1I30M2D4M1D4M1D9M1D5M1I5M1I3M2I6M1I15M1D20M1I3M1I13M5I18M2I10M1I7M1D1M1I38M1D39M1I5M1I6M1588S,36,188;chr14,50254065,-,2S19M1D7M1I2M1D8M2I9M1I8M1I1M1I6M1I2M1I2M1D32M1I12M1I7M1D5M1D9M1I15M1I7M1D11M1I5M1I28M1I13M1I12M1I15M1I2M1D1M1I6M1I19M1D2M1D1M1D5M1I8M1I9M1I22M1D3M2D6M1I6M1D5M1I9M1I2M1I1M1I22M2I10M1D12M1I6M1I24M1I4M1I5M2I4M1I5M4I2M1I6M1D8M1I10M2D7M1I8M1D18M1I7M1I4M1D5M1I9M1I2M1D6M1I12M1I18M1D27M1I1M1D7M1D1M1D11M1D15M1I13M1I1M1I3M1I9M1I6M1D23M1I9M1I1M1I3M1D12M1I1M2I12M1I9M1I4M1I2M1D6M1I1M1I33M1I9M2I5M1I8M1I20M1D18M1I10M1I6M1I22M1I16M1I6M3I3M2D9M5I23M1I3M1D10M1D21M1I5M1I7M1I8M1I14M7I8M4I1M1I15M1I5M3D31M1I15M1I26M4624S,38,166;chr8,123906302,+,157S3M1I2M1I8M1I2M1I2M1I3M1I4M1D14M1I2M2I3M1I1M1I7M1I5M1D13M2I3M1I7M1I2M1I9M2D5M1D11M1I2M1D10M2I4M1I3M1I5M1D5M1I4M1I3M1D4M1I5M1I45M1I4M1I12M1D3M1I1M1I4M2I27M1I3M1D4M1I3M1I5M1I2M1D4M1I6M1D13M1I12M1I9M1I11M1I18M2I4M1I12M1D7M1I40M2I3M1I1M1I8M1D26M1I6M1I15M1D3M2I16M2I7M1I5M1D8M1I6M1D16M1I22M1I6M1I3M1D1M1D3M1I10M1D6M1D10M1I1M1D16M1I3M1I1M1I3M1I35M1I5M1I33M1I13M1I22M2I22M1D5M1I1M1D4M1D21M1I15M1I3M1I5M1I9M1D3M1I10M1D13M1D2M1I4M1I3M1D8M2I5M1D2M3I3M1D20M2I10M1I16M1I5M1I1M2I4M2I2M1I14M1D22M1I7M1I4M1I13M1I1M1I5M1D5M1D22M1I7M1I13M1I17M1I15M1I26M1I1M1D11M2D8M1D20M1I7M1D4M1I8M1D8M4409S,30,166;chr8,126480844,-,4239S17M1D6M1I30M1I11M1D4M1I7M1I11M1I9M1I50M1I8M1I10M1411S,2,11;\tKB:f:24.508675\tSB:f:24.508675\tID:i:150690\tMD:Z:24C15G1^G31^C31^A101C24^T17^G36^G11T6^C28^C3^T11^G9A30A1^C19C0A3T4C1T2^A2T3A1T1^C3A2^A0C3^CC0A1T1A1^AA0T0A3T1^A0C14T0T1^A1C4^A0A0A2C9^GG1^A9^T29^C47^G13G92C2^C115A5^A125^C23^T11T10A16^G5^A33^A30^A39^A2^A7^A28^A9^C39^A5^A51^A7\tQE:i:2676\tXE:i:1426\tXI:f:0.8567\tNM:i:203\tXR:i:1374\tAS:i:1426\tQS:i:1302\tXS:i:0\tCV:f:23.60419\tSV:i:2"; + String saTag = "chr17,64042266,-,2676S17M1D10M1I14M1D26M1I2M1I20M2I1M1I23M1I11M1D8M1D10M1D16M1I3M1I3M1D1M1I4M1I11M1I2M1I6M1D10M1I11M1D3M1D11M1D8M3I2M1I14M1I7M4I12M1I3M1I9M1D13M1D23M1I4M1I13M1D12M1I38M1I11M1I5M1D11M1I17M1I14M1D12M1D8M1I2M1I3M1D12M1I2M2I8M1I19M1D1M2D11M1D25M1I2M1I21M1I28M1I4M1D4M1I1M2I4M1I22M1I5M1I3M1D3M1I9M2I6M1I9M1D2M1D4M1D11M1D2M1I18M1D2M1I13M1I23M1I17M1I7M1I2M1D4M1I21M1I25M1D12M1I3M1I29M1D2M1D1M1D6M1D8M1I8M1D17M1D13M1I54M1D5M1I4M1D12M2I1M1I3M1I5M1I3M1I7M1I9M1I1M2I25M1I2M1D5M1D18M1I27M1I2M1I8M1D9M1D3M1I11M1D6M1I2M1I4M2I3M2I5M1I13M2I4M1I30M1D3M1I9M1I30M2D4M1D4M1D9M1D5M1I5M1I3M2I6M1I15M1D20M1I3M1I13M5I18M2I10M1I7M1D1M1I38M1D39M1I5M1I6M1588S,36,188;14,50254065,-,2S19M1D7M1I2M1D8M2I9M1I8M1I1M1I6M1I2M1I2M1D32M1I12M1I7M1D5M1D9M1I15M1I7M1D11M1I5M1I28M1I13M1I12M1I15M1I2M1D1M1I6M1I19M1D2M1D1M1D5M1I8M1I9M1I22M1D3M2D6M1I6M1D5M1I9M1I2M1I1M1I22M2I10M1D12M1I6M1I24M1I4M1I5M2I4M1I5M4I2M1I6M1D8M1I10M2D7M1I8M1D18M1I7M1I4M1D5M1I9M1I2M1D6M1I12M1I18M1D27M1I1M1D7M1D1M1D11M1D15M1I13M1I1M1I3M1I9M1I6M1D23M1I9M1I1M1I3M1D12M1I1M2I12M1I9M1I4M1I2M1D6M1I1M1I33M1I9M2I5M1I8M1I20M1D18M1I10M1I6M1I22M1I16M1I6M3I3M2D9M5I23M1I3M1D10M1D21M1I5M1I7M1I8M1I14M7I8M4I1M1I15M1I5M3D31M1I15M1I26M4624S,38,166;8,123906302,+,157S3M1I2M1I8M1I2M1I2M1I3M1I4M1D14M1I2M2I3M1I1M1I7M1I5M1D13M2I3M1I7M1I2M1I9M2D5M1D11M1I2M1D10M2I4M1I3M1I5M1D5M1I4M1I3M1D4M1I5M1I45M1I4M1I12M1D3M1I1M1I4M2I27M1I3M1D4M1I3M1I5M1I2M1D4M1I6M1D13M1I12M1I9M1I11M1I18M2I4M1I12M1D7M1I40M2I3M1I1M1I8M1D26M1I6M1I15M1D3M2I16M2I7M1I5M1D8M1I6M1D16M1I22M1I6M1I3M1D1M1D3M1I10M1D6M1D10M1I1M1D16M1I3M1I1M1I3M1I35M1I5M1I33M1I13M1I22M2I22M1D5M1I1M1D4M1D21M1I15M1I3M1I5M1I9M1D3M1I10M1D13M1D2M1I4M1I3M1D8M2I5M1D2M3I3M1D20M2I10M1I16M1I5M1I1M2I4M2I2M1I14M1D22M1I7M1I4M1I13M1I1M1I5M1D5M1D22M1I7M1I13M1I17M1I15M1I26M1I1M1D11M2D8M1D20M1I7M1D4M1I8M1D8M4409S,30,166;8,126480844,-,4239S17M1D6M1I30M1I11M1D4M1I7M1I11M1I9M1I50M1I8M1I10M1411S,2,11"; + record.setAttribute(SAMTag.SA, "3,10000,+,100S50M50S,200,0;3,10000,-,50M250S,0,0"); + final Alignment samAlignment = new SAMAlignment(record); + String read2 = "primary\t16\t2\t10000\t100\t200S100M\t*\t0\t0\t*\t*\tSA:Z:3,10000,+,100S50M50S,200,*;3,20000,+,50M250S,0,*;\tRG:Z:x\n"; + SupplementaryAlignmentDiagramDialog frame = new SupplementaryAlignmentDiagramDialog(null, samAlignment, new Dimension(500, 150)); + frame.setVisible(true); + } +} diff --git a/src/main/java/org/broad/igv/ui/util/ApplicationStatusBar.java b/src/main/java/org/broad/igv/ui/util/ApplicationStatusBar.java index 4eb3b4f8f5..4c6c9b966a 100644 --- a/src/main/java/org/broad/igv/ui/util/ApplicationStatusBar.java +++ b/src/main/java/org/broad/igv/ui/util/ApplicationStatusBar.java @@ -27,6 +27,7 @@ package org.broad.igv.ui.util; import com.jidesoft.swing.JideBoxLayout; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.StopEvent; import org.broad.igv.logging.*; import org.broad.igv.oauth.OAuthProvider; @@ -34,13 +35,10 @@ import org.broad.igv.ui.FontManager; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; -import org.broad.igv.ui.IGV; //import org.broad.igv.event.StopEvent; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.text.NumberFormat; import java.util.TimerTask; @@ -157,7 +155,7 @@ public void enableStopButton(boolean enable) { } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof OAuthProvider.AuthStateEvent) { String msg = ""; diff --git a/src/main/java/org/broad/igv/variant/VariantTrack.java b/src/main/java/org/broad/igv/variant/VariantTrack.java index 97910f08d7..1e3633edf1 100644 --- a/src/main/java/org/broad/igv/variant/VariantTrack.java +++ b/src/main/java/org/broad/igv/variant/VariantTrack.java @@ -31,6 +31,7 @@ import htsjdk.tribble.Feature; import htsjdk.variant.variantcontext.GenotypeType; import org.broad.igv.Globals; +import org.broad.igv.event.IGVEvent; import org.broad.igv.event.IGVEventBus; import org.broad.igv.event.IGVEventObserver; import org.broad.igv.event.TrackGroupEvent; @@ -1190,7 +1191,7 @@ public void setSquishedHeight(int squishedHeight) { } @Override - public void receiveEvent(Object event) { + public void receiveEvent(IGVEvent event) { if (event instanceof TrackGroupEvent) { setupGroupsFromAttributes(); } diff --git a/src/main/java/org/broad/igv/variant/util/PedigreeUtils.java b/src/main/java/org/broad/igv/variant/util/PedigreeUtils.java index 4728b28772..0bfd2baef2 100644 --- a/src/main/java/org/broad/igv/variant/util/PedigreeUtils.java +++ b/src/main/java/org/broad/igv/variant/util/PedigreeUtils.java @@ -76,7 +76,7 @@ public static void parseTrioFile(String path) throws IOException { } } - IGVEventBus.getInstance().post(new TrackGroupEvent(null)); + IGVEventBus.getInstance().post(new TrackGroupEvent()); } finally { if (reader != null) reader.close(); } diff --git a/src/test/java/org/broad/igv/feature/FeatureDBTest.java b/src/test/java/org/broad/igv/feature/FeatureDBTest.java index 7a00e9ba55..5e5a1ff4a9 100644 --- a/src/test/java/org/broad/igv/feature/FeatureDBTest.java +++ b/src/test/java/org/broad/igv/feature/FeatureDBTest.java @@ -25,13 +25,11 @@ package org.broad.igv.feature; -import junit.framework.AssertionFailedError; import org.broad.igv.AbstractHeadlessTest; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -76,7 +74,7 @@ public void setUpTest() throws Exception { @Test public void testFeaturesMap() throws Exception { - Map> fMap = FeatureDB.getFeaturesMap(CHECK_STR); + Map> fMap = FeatureDB.getFeaturesMap(CHECK_STR); for (String k : fMap.keySet()) { @@ -87,7 +85,7 @@ public void testFeaturesMap() throws Exception { @Test public void testFeatureListSize() throws Exception { - List features = FeatureDB.getFeaturesList(CHECK_STR, 3); + List features = FeatureDB.getFeaturesList(CHECK_STR, 3); assertEquals(3, features.size()); features = FeatureDB.getFeaturesList(CHECK_STR, LARGE); @@ -98,8 +96,8 @@ public void testFeatureListSize() throws Exception { @Test public void testFeatureList() throws Exception { - List features = FeatureDB.getFeaturesList(CHECK_STR, LARGE); - for (NamedFeature f : features) { + List features = FeatureDB.getFeaturesList(CHECK_STR, LARGE); + for (IGVNamedFeature f : features) { assertTrue(f.getName().startsWith(CHECK_STR)); assertNotNull(FeatureDB.getFeature(f.getName())); } @@ -109,21 +107,21 @@ public void testFeatureList() throws Exception { @Test public void testMultiRetrieve() throws Exception { String checkstr = "EGFLAM"; - Map> fMap = FeatureDB.getFeaturesMap(checkstr); - List data = fMap.get(checkstr); + Map> fMap = FeatureDB.getFeaturesMap(checkstr); + List data = fMap.get(checkstr); assertEquals(4, data.size()); } @Test public void testMultipleEntries() throws Exception { String checkstr = "EG"; - Map> fMap = FeatureDB.getFeaturesMap(checkstr); + Map> fMap = FeatureDB.getFeaturesMap(checkstr); for (String k : fMap.keySet()) { - List data = fMap.get(k); + List data = fMap.get(k); //System.out.println("key " + k + " has " + data.size()); for (int ii = 0; ii < data.size() - 1; ii++) { - NamedFeature feat1 = data.get(ii); - NamedFeature feat2 = data.get(ii + 1); + IGVNamedFeature feat1 = data.get(ii); + IGVNamedFeature feat2 = data.get(ii + 1); int len1 = feat1.getEnd() - feat1.getStart(); int len2 = feat2.getEnd() - feat2.getStart(); assertTrue("Data for key " + k + " not sorted", len1 >= len2); diff --git a/src/test/java/org/broad/igv/feature/aa/CodonTest.java b/src/test/java/org/broad/igv/feature/aa/CodonTest.java index 6731d7abf0..fe78456309 100644 --- a/src/test/java/org/broad/igv/feature/aa/CodonTest.java +++ b/src/test/java/org/broad/igv/feature/aa/CodonTest.java @@ -28,8 +28,7 @@ import org.broad.igv.AbstractHeadlessTest; import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.FeatureDB; -import org.broad.igv.feature.NamedFeature; -import org.broad.igv.feature.aa.Codon; +import org.broad.igv.feature.IGVNamedFeature; import org.junit.Ignore; import org.junit.Test; @@ -70,7 +69,7 @@ public void testGetCodonNeg() { tstGetCodon(KRAS, expected); } - public void tstGetCodon(NamedFeature feature, char[] expected) { + public void tstGetCodon(IGVNamedFeature feature, char[] expected) { BasicFeature bf = (BasicFeature) feature; for (int pos = 0; pos < expected.length; pos++) { @@ -90,8 +89,8 @@ public void testGetCodonInvalid() { //valid for these ids: //NM_003661, NM_145343, NM_001136540 - List featuresList = FeatureDB.getFeaturesList(geneName, 50, false); - for (NamedFeature feat : featuresList) { + List featuresList = FeatureDB.getFeaturesList(geneName, 50, false); + for (IGVNamedFeature feat : featuresList) { BasicFeature bf = (BasicFeature) feat; System.out.println(bf.getIdentifier()); Codon c = bf.getCodon(genome, bf.getChr(), proteinPos); diff --git a/src/test/java/org/broad/igv/ui/action/SearchCommandTest.java b/src/test/java/org/broad/igv/ui/action/SearchCommandTest.java index b05de789c8..c5b2ab4d56 100644 --- a/src/test/java/org/broad/igv/ui/action/SearchCommandTest.java +++ b/src/test/java/org/broad/igv/ui/action/SearchCommandTest.java @@ -29,6 +29,7 @@ import org.broad.igv.AbstractHeadlessTest; import org.broad.igv.feature.genome.ChromAlias; import org.broad.igv.feature.genome.ChromAliasSource; +import org.broad.igv.feature.genome.Genome; import org.broad.igv.util.TestUtils; import org.junit.Before; import org.junit.Ignore; diff --git a/src/test/java/org/broad/igv/util/TestUtils.java b/src/test/java/org/broad/igv/util/TestUtils.java index 74de50e9d6..647eab0b84 100644 --- a/src/test/java/org/broad/igv/util/TestUtils.java +++ b/src/test/java/org/broad/igv/util/TestUtils.java @@ -32,6 +32,7 @@ import org.broad.igv.feature.Chromosome; import org.broad.igv.feature.NamedFeature; import org.broad.igv.feature.genome.ChromAliasDefaults; +import org.broad.igv.feature.IGVNamedFeature; import org.broad.igv.feature.genome.Genome; import org.broad.igv.ui.commandbar.GenomeListManager; import org.broad.igv.prefs.PreferencesManager; @@ -295,7 +296,7 @@ public static void assertFeaturesEqual(Feature exp, Feature act) { * @param exp * @param act */ - public static void assertNamedFeaturesEqual(NamedFeature exp, NamedFeature act) { + public static void assertNamedFeaturesEqual(IGVNamedFeature exp, IGVNamedFeature act) { assertFeaturesEqual(exp, act); assertEquals(exp.getName().toUpperCase(), act.getName().toUpperCase()); }