diff --git a/src/main/java/org/broad/igv/feature/Strand.java b/src/main/java/org/broad/igv/feature/Strand.java index 8c8666284e..b95859e8c7 100644 --- a/src/main/java/org/broad/igv/feature/Strand.java +++ b/src/main/java/org/broad/igv/feature/Strand.java @@ -57,4 +57,6 @@ public String toShortString() { public String toString() { return this == POSITIVE ? "+" : this == NEGATIVE ? "-" : ""; } + + } diff --git a/src/main/java/org/broad/igv/feature/genome/GenomeManager.java b/src/main/java/org/broad/igv/feature/genome/GenomeManager.java index ac5d5c04e4..81f9a816d3 100644 --- a/src/main/java/org/broad/igv/feature/genome/GenomeManager.java +++ b/src/main/java/org/broad/igv/feature/genome/GenomeManager.java @@ -53,6 +53,7 @@ import org.broad.igv.track.FeatureTrack; import org.broad.igv.track.Track; import org.broad.igv.ui.IGV; +import org.broad.igv.ui.PanelName; import org.broad.igv.ui.commandbar.GenomeListManager; import org.broad.igv.ui.panel.FrameManager; import org.broad.igv.ui.util.MessageUtils; @@ -71,10 +72,12 @@ import java.net.SocketException; import java.net.URL; import java.net.URLDecoder; -import java.util.LinkedHashMap; +import java.util.*; import java.util.List; -import java.util.Map; -import java.util.Set; + +import static org.broad.igv.prefs.Constants.SHOW_SINGLE_TRACK_PANE_KEY; +import static org.broad.igv.ui.IGV.DATA_PANEL_NAME; +import static org.broad.igv.ui.IGV.FEATURE_PANEL_NAME; /** * @author jrobinso @@ -200,7 +203,7 @@ public Genome loadGenome(String genomePath, ProgressMonitor monitor) throws IOEx // Load user-defined chr aliases, if any. This is done last so they have priority try { String aliasPath = (new File(DirectoryManager.getGenomeCacheDirectory(), newGenome.getId() + "_alias.tab")).getAbsolutePath(); - if(!(new File(aliasPath)).exists()) { + if (!(new File(aliasPath)).exists()) { aliasPath = (new File(DirectoryManager.getGenomeCacheDirectory(), newGenome.getId() + "_alias.tab.txt")).getAbsolutePath(); } if ((new File(aliasPath)).exists()) { @@ -244,6 +247,12 @@ public Genome loadGenome(String genomePath, ProgressMonitor monitor) throws IOEx } } + /** + * Load and initialize the track objects from the genome's track resource locators. Does not add the tracks + * to the IGV instance. + * + * @param genome + */ public void loadGenomeAnnotations(Genome genome) { List resources = genome.getAnnotationResources(); if (resources != null) { @@ -255,16 +264,31 @@ public void loadGenomeAnnotations(Genome genome) { } catch (DataLoadException e) { log.error("Error loading genome annotations", e); } - } + }// genome.setAnnotationTracks(annotationTracks); } restoreGenomeTracks(genome); IGV.getInstance().repaint(); } + /** + * Add a genomes tracks to the IGV instance. + * + * @param genome + */ public void restoreGenomeTracks(Genome genome) { + + IGV.getInstance().setSequenceTrack(); + FeatureTrack geneFeatureTrack = genome.getGeneTrack(); // Can be null - IGV.getInstance().setGenomeTracks(geneFeatureTrack); + if (geneFeatureTrack != null) { + PanelName panelName = PreferencesManager.getPreferences().getAsBoolean(SHOW_SINGLE_TRACK_PANE_KEY) ? + PanelName.DATA_PANEL : PanelName.FEATURE_PANEL; + geneFeatureTrack.setAttributeValue(Globals.TRACK_NAME_ATTRIBUTE, geneFeatureTrack.getName()); + geneFeatureTrack.setAttributeValue(Globals.TRACK_DATA_FILE_ATTRIBUTE, ""); + geneFeatureTrack.setAttributeValue(Globals.TRACK_DATA_TYPE_ATTRIBUTE, geneFeatureTrack.getTrackType().toString()); + IGV.getInstance().addTracks(Arrays.asList(geneFeatureTrack), panelName); + } Map> annotationTracks = currentGenome.getAnnotationTracks(); if (annotationTracks != null) { diff --git a/src/main/java/org/broad/igv/session/IGVSessionReader.java b/src/main/java/org/broad/igv/session/IGVSessionReader.java index c4483c780b..4e8f93353b 100644 --- a/src/main/java/org/broad/igv/session/IGVSessionReader.java +++ b/src/main/java/org/broad/igv/session/IGVSessionReader.java @@ -85,31 +85,22 @@ public class IGVSessionReader implements SessionReader { private static Map attributeSynonymMap = new HashMap(); private static WeakReference currentReader; - Collection dataFiles; //package-private for unit testing - private Collection missingDataFiles; - private boolean panelElementPresent = false; // Until proven otherwise + private IGV igv; private int version; private String genomePath; + private Collection dataFiles; + private Collection missingDataFiles; + private boolean panelElementPresent = false; // Flag indicating if "Panel" sections are present + private TrackFilter filter; // There is a single TrackFilter object per session, usually null /** - * There is a single TrackFilter object per session, usually null - */ - TrackFilter filter; - - private IGV igv; - - /** - * Map of track id -> track. It is important to maintain the order in which tracks are added, thus - * the use of LinkedHashMap. We add tracks here when loaded and remove them when attributes are specified. - */ - private final Map> leftoverTrackDictionary = Collections.synchronizedMap(new LinkedHashMap()); - - /** - * List of combined data source tracks. Processing of data sources has to be deferred until all tracks + * List of combined data source tracks. Processing of combined data sources has to be deferred until all tracks * are loaded */ private final List> combinedDataSourceTracks = new ArrayList<>(); + private Set allocatedToPanel; // List of tracks allocated to panels, if Panel elements are present. + /** * Map of id -> track, for second pass through when tracks reference each other */ @@ -117,37 +108,6 @@ public class IGVSessionReader implements SessionReader { private final Set erroredResources = Collections.synchronizedSet(new HashSet<>()); - public List getTracksById(String trackId) { - List tracks = allTracks.get(trackId); - if (tracks == null) { - // ID is usually a full file path or URL. See if we can find the track with just filename - if (!FileUtils.isRemote(trackId)) { - String fn = (new File(trackId)).getName(); - tracks = allTracks.get(fn); - } - if (tracks == null) { - // If still no match search for legacy gene annotation specifier - Genome genome = GenomeManager.getInstance().getCurrentGenome(); - String legacyGeneTrackID = genome.getId() + "_genes"; - if (trackId.equals(legacyGeneTrackID)) { - if (genome.getGeneTrack() != null) { - return Arrays.asList(genome.getGeneTrack()); - } else if (genome.getAnnotationTracks() != null && genome.getAnnotationTracks().size() > 0) { - return genome.getAnnotationTracks().values().iterator().next(); - } - } - } - } - return tracks; - } - - - /** - * Map of full path -> relative path. - */ - Map fullToRelPathMap = new HashMap(); - - private Track geneTrack = null; private boolean hasTrackElments; static { @@ -189,13 +149,25 @@ public void loadSession(InputStream inputStream, Session session, String session } Node rootNode = nodes.item(0); - // Recursively process all nodes, starting with the root + // Walk tree processing all nodes, starting with the root processRootNode(session, rootNode, sessionPath); processCombinedDataSourceTracks(); - // Add tracks not explicitly allocated to panels. This most often happens with hand created sessions lacking a panels section. - addLeftoverTracks(leftoverTrackDictionary.values()); + // Add tracks not explicitly allocated to panels. This can happen if a track resource path changes after + // session created, and session is hand-editted. It can also happen if the annotation paths for a genome change + // after session creation. + if(allocatedToPanel != null) { + List unallocatedTracks = new ArrayList<>(); + for (List tracks : allTracks.values()) { + for (Track t : tracks) { + if (!allocatedToPanel.contains(t)) { + unallocatedTracks.add(t); + } + } + } + addUnallocatedTracks(unallocatedTracks); + } if (session.getGroupTracksBy() != null && session.getGroupTracksBy().length() > 0) { @@ -231,10 +203,9 @@ private void processRootNode(Session session, Node node, String sessionPath) { return; } - Element element = (Element) node; - process(session, node, sessionPath); + Element rootElement = (Element) node; - String versionString = getAttribute(element, SessionAttribute.VERSION); + String versionString = getAttribute(rootElement, SessionAttribute.VERSION); try { version = Integer.parseInt(versionString); } catch (NumberFormatException e) { @@ -242,13 +213,14 @@ private void processRootNode(Session session, Node node, String sessionPath) { } // Load the genome, which can be an ID, or a path or URL to a .genome or indexed fasta file. - String genomeId = getAttribute(element, SessionAttribute.GENOME); - + String genomeId = getAttribute(rootElement, SessionAttribute.GENOME); if (genomeId != null && genomeId.length() > 0) { if (genomeId.equals(GenomeManager.getInstance().getGenomeId())) { + // No genome change igv.resetSession(sessionPath); GenomeManager.getInstance().restoreGenomeTracks(GenomeManager.getInstance().getCurrentGenome()); } else { + // New genome try { GenomeListItem item = GenomeListManager.getInstance().getGenomeListItem(genomeId); if (item != null) { @@ -256,7 +228,7 @@ private void processRootNode(Session session, Node node, String sessionPath) { GenomeManager.getInstance().loadGenome(item.getPath(), null); } else { genomePath = genomeId; - if (!ParsingUtils.fileExists(genomePath)) { + if (!FileUtils.isRemote(genomePath) && !ParsingUtils.fileExists(genomePath)) { genomePath = getAbsolutePath(genomeId, sessionPath); } GenomeManager.getInstance().loadGenome(genomePath, null); @@ -268,10 +240,10 @@ private void processRootNode(Session session, Node node, String sessionPath) { } } - session.setLocus(getAttribute(element, SessionAttribute.LOCUS)); - session.setGroupTracksBy(getAttribute(element, SessionAttribute.GROUP_TRACKS_BY)); + session.setLocus(getAttribute(rootElement, SessionAttribute.LOCUS)); + session.setGroupTracksBy(getAttribute(rootElement, SessionAttribute.GROUP_TRACKS_BY)); - String nextAutoscaleGroup = getAttribute(element, SessionAttribute.NEXT_AUTOSCALE_GROUP); + String nextAutoscaleGroup = getAttribute(rootElement, SessionAttribute.NEXT_AUTOSCALE_GROUP); if (nextAutoscaleGroup != null) { try { session.setNextAutoscaleGroup(Integer.parseInt(nextAutoscaleGroup)); @@ -280,7 +252,7 @@ private void processRootNode(Session session, Node node, String sessionPath) { } } - String removeEmptyTracks = getAttribute(element, "removeEmptyTracks"); + String removeEmptyTracks = getAttribute(rootElement, "removeEmptyTracks"); if (removeEmptyTracks != null) { try { Boolean b = Boolean.parseBoolean(removeEmptyTracks); @@ -291,40 +263,31 @@ private void processRootNode(Session session, Node node, String sessionPath) { } session.setVersion(version); - NodeList elements = element.getChildNodes(); + NodeList elements = rootElement.getChildNodes(); process(session, elements, sessionPath); - // ReferenceFrame.getInstance().invalidateLocationScale(); } - //TODO Check to make sure tracks are not being created twice - //TODO -- DONT DO THIS FOR NEW SESSIONS - private void addLeftoverTracks(Collection> tmp) { + private void addUnallocatedTracks(List tracks) { Map trackPanelCache = new HashMap(); - if (version < 3 || !panelElementPresent) { - log.debug("Adding \"leftover\" tracks"); - - //For resetting track panels later - List> trackPanelAttrs = null; - trackPanelAttrs = igv.getTrackPanelAttrs(); - - for (List tracks : tmp) { - for (Track track : tracks) { - if (track == geneTrack) { - igv.setGenomeTracks(track); - } else if (track.getResourceLocator() != null) { - TrackPanel panel = trackPanelCache.get(track.getResourceLocator().getPath()); - if (panel == null) { - panel = igv.getPanelFor(track); - trackPanelCache.put(track.getResourceLocator().getPath(), panel); - } - panel.addTrack(track); - } + + log.debug("Adding \"leftover\" tracks"); + //For resetting track panels later + List> trackPanelAttrs = null; + trackPanelAttrs = igv.getTrackPanelAttrs(); + + for (Track track : tracks) { + if (track.getResourceLocator() != null) { + TrackPanel panel = trackPanelCache.get(track.getResourceLocator().getPath()); + if (panel == null) { + panel = igv.getPanelFor(track); + trackPanelCache.put(track.getResourceLocator().getPath(), panel); } + panel.addTrack(track); } - - igv.resetPanelHeights(trackPanelAttrs.get(0), trackPanelAttrs.get(1)); } + + igv.resetPanelHeights(trackPanelAttrs.get(0), trackPanelAttrs.get(1)); } @@ -434,9 +397,9 @@ private void processResources(Session session, Element element, String sessionPa Runnable runnable = () -> { try { + // igv.load() loads and initializes tracks, but does not allocate them to panels. List tracks = igv.load(locator); for (Track track : tracks) { - if (track == null) { log.info("Null track for resource " + locator.getPath()); continue; @@ -447,21 +410,19 @@ private void processResources(Session session, Element element, String sessionPa log.info("Null track id for resource " + locator.getPath()); continue; } - // id is often an absolute file path. Use just the filename if unique - if (!FileUtils.isRemote(id)) { - String fn = (new File(id)).getName(); - if (!allTracks.containsKey(fn)) { - id = fn; - } - } - List trackList = leftoverTrackDictionary.get(id); - if (trackList == null) { - trackList = new ArrayList(); - leftoverTrackDictionary.put(id, trackList); - allTracks.put(id, trackList); + // id is often an absolute file path. Use just the filename if unique +// if (!FileUtils.isRemote(id)) { +// String fn = (new File(id)).getName(); +// if (!allTracks.containsKey(fn)) { +// id = fn; +// } +// } + + if(!allTracks.containsKey(id)) { + allTracks.put(id, new ArrayList<>()); } - trackList.add(track); + allTracks.get(id).add(track); } } catch (Exception e) { erroredResources.add(locator.getPath()); @@ -552,8 +513,6 @@ void processResource(Session session, Element element, String sessionPath) { path : getAbsolutePath(path, sessionPath); - fullToRelPathMap.put(absolutePath, path); - ResourceLocator resourceLocator = new ResourceLocator(serverURL, absolutePath); if (index != null) resourceLocator.setIndexPath(index); @@ -806,16 +765,19 @@ private void processPanel(Session session, Element element, String sessionPath) if (panelElementPresent == false) { // First panel to be processed, do this only once. - // Add any tracks loaded as a side effect of loading genome, these need to be removed and remembered final List tmp = igv.getAllTracks(); for (Track track : tmp) { final String id = track.getId(); - final List trackList = Arrays.asList(track); - leftoverTrackDictionary.put(id, trackList); - this.allTracks.put(id, trackList); + + if (!allTracks.containsKey(id)) { + allTracks.put(id, new ArrayList<>()); + } + allTracks.get(id).add(track); } + allocatedToPanel = new LinkedHashSet<>(); + // Tracks, if any, will be placed back as prescribed by panel elements removeAllTracksFromPanels(); } @@ -908,63 +870,52 @@ private List processTrack(Session session, Element element, String sessio if (matchedTracks != null) { for (final Track track : matchedTracks) { track.unmarshalXML(element, version); + allocatedToPanel.add(track); } - leftoverTrackDictionary.remove(id); } else { // No match found, element represents either (1) a track from a file that errored during load, for example // a file that has been deleted, or (2) a track not created from "Resource" or genome load. These include // reference sequence, combined, and merged tracks. - // Check for errors during load -// String absPath = getAbsolutePath(id, sessionPath); -// if (erroredResources.contains(id) || erroredResources.contains(absPath)) { -// return Collections.emptyList(); -// } - -// String className = getAttribute(element, "clazz"); -// if (className != null && -// (className.contains("MergedTracks") || -// className.contains("CombinedDataTrack") || -// className.contains("SequenceTrack") || -// className.contains("BlatTrack"))) { String className = getAttribute(element, "clazz"); - try { - Track track = createTrack(className, element); - if (track != null) { + if (resourceIndpendentTracks.stream().anyMatch(c -> className.contains(c))) { + try { + Track track = createTrack(className, element); + if (track != null) { - track.unmarshalXML(element, version); - matchedTracks = Arrays.asList(track); - allTracks.put(track.getId(), matchedTracks); // Important for second pass + track.unmarshalXML(element, version); + matchedTracks = Arrays.asList(track); + allTracks.put(track.getId(), matchedTracks); // Important for second pass - // Special tracks - if (className.contains("CombinedDataTrack")) { - combinedDataSourceTracks.add(new Pair(track, element)); - } + // Special tracks + if (className.contains("CombinedDataTrack")) { + combinedDataSourceTracks.add(new Pair(track, element)); + } - if (className.contains("MergedTracks")) { - List memberTracks = processChildTracks(session, element, sessionPath); - ((MergedTracks) track).setMemberTracks(memberTracks); - - NodeList nodeList = element.getElementsByTagName("DataRange"); - if (nodeList != null && nodeList.getLength() > 0) { - Element dataRangeElement = (Element) nodeList.item(0); - try { - DataRange dataRange = new DataRange(dataRangeElement, version); - track.setDataRange(dataRange); - } catch (Exception e) { - log.error("Unrecognized DataRange"); + if (className.contains("MergedTracks")) { + List memberTracks = processChildTracks(session, element, sessionPath); + ((MergedTracks) track).setMemberTracks(memberTracks); + + NodeList nodeList = element.getElementsByTagName("DataRange"); + if (nodeList != null && nodeList.getLength() > 0) { + Element dataRangeElement = (Element) nodeList.item(0); + try { + DataRange dataRange = new DataRange(dataRangeElement, version); + track.setDataRange(dataRange); + } catch (Exception e) { + log.error("Unrecognized DataRange"); + } } } + } else { + log.warn("Warning. No tracks were found with id: " + id + " in session file"); } - } else { - log.warn("Warning. No tracks were found with id: " + id + " in session file"); + } catch (Exception e) { + log.error("Error restoring track: " + element.toString(), e); + MessageUtils.showMessage("Error loading track: " + element.toString()); } - } catch (Exception e) { - log.error("Error restoring track: " + element.toString(), e); - MessageUtils.showMessage("Error loading track: " + element.toString()); } } - //} NodeList elements = element.getChildNodes(); process(session, elements, sessionPath); @@ -1238,4 +1189,31 @@ private String getAbsolutePath(String path, String sessionPath) { return absolute; } + public List getTracksById(String trackId) { + List tracks = allTracks.get(trackId); + if (tracks == null) { + // ID is usually a full file path or URL. See if we can find the track with just filename + if (!FileUtils.isRemote(trackId)) { + String fn = (new File(trackId)).getName(); + tracks = allTracks.get(fn); + } + if (tracks == null) { + // If still no match search for legacy gene annotation specifier + Genome genome = GenomeManager.getInstance().getCurrentGenome(); + String legacyGeneTrackID = genome.getId() + "_genes"; + if (trackId.equals(legacyGeneTrackID)) { + if (genome.getGeneTrack() != null) { + return Arrays.asList(genome.getGeneTrack()); + } else if (genome.getAnnotationTracks() != null && genome.getAnnotationTracks().size() > 0) { + return genome.getAnnotationTracks().values().iterator().next(); + } + } + } + } + return tracks; + } + + private static Set resourceIndpendentTracks = new HashSet<>(Arrays.asList("MergedTracks", "CombinedDataTrack", + "SequenceTrack", "BlatTrack", "MotifTrack")); + } diff --git a/src/main/java/org/broad/igv/track/MotifTrack.java b/src/main/java/org/broad/igv/track/MotifTrack.java index e8d2724354..78c735fc85 100644 --- a/src/main/java/org/broad/igv/track/MotifTrack.java +++ b/src/main/java/org/broad/igv/track/MotifTrack.java @@ -44,7 +44,7 @@ public void marshalXML(Document document, Element element) { public void unmarshalXML(Element element, Integer version) { super.unmarshalXML(element, version); this.pattern = element.getAttribute("pattern"); - this.strand = Strand.valueOf(element.getAttribute("strand")); + this.strand = Strand.fromString(element.getAttribute("strand")); init(); } } diff --git a/src/main/java/org/broad/igv/ui/IGV.java b/src/main/java/org/broad/igv/ui/IGV.java index 6ac6e964d9..b7fa6e962f 100644 --- a/src/main/java/org/broad/igv/ui/IGV.java +++ b/src/main/java/org/broad/igv/ui/IGV.java @@ -43,7 +43,6 @@ import org.broad.igv.batch.CommandListener; import org.broad.igv.event.*; import org.broad.igv.exceptions.DataLoadException; -import org.broad.igv.feature.MaximumContigGenomeException; import org.broad.igv.feature.Range; import org.broad.igv.feature.RegionOfInterest; import org.broad.igv.feature.Strand; @@ -1641,26 +1640,21 @@ public void deleteTracks(Collection tracksToRemove) { /** * Add gene and sequence tracks. This is called upon switching genomes. * - * @param newGeneTrack * @param */ - public void setGenomeTracks(Track newGeneTrack) { + public void setSequenceTrack() { TrackPanel panel = PreferencesManager.getPreferences().getAsBoolean(SHOW_SINGLE_TRACK_PANE_KEY) ? getTrackPanel(DATA_PANEL_NAME) : getTrackPanel(FEATURE_PANEL_NAME); SequenceTrack newSeqTrack = new SequenceTrack("Reference sequence"); panel.addTrack(newSeqTrack); - if (newGeneTrack != null) { - newGeneTrack.setAttributeValue(Globals.TRACK_NAME_ATTRIBUTE, newGeneTrack.getName()); - newGeneTrack.setAttributeValue(Globals.TRACK_DATA_FILE_ATTRIBUTE, ""); - newGeneTrack.setAttributeValue(Globals.TRACK_DATA_TYPE_ATTRIBUTE, newGeneTrack.getTrackType().toString()); - panel.addTrack(newGeneTrack); - } - } - - public boolean hasSequenceTrack() { - return getSequenceTrack() != null; +// if (newGeneTrack != null) { +// newGeneTrack.setAttributeValue(Globals.TRACK_NAME_ATTRIBUTE, newGeneTrack.getName()); +// newGeneTrack.setAttributeValue(Globals.TRACK_DATA_FILE_ATTRIBUTE, ""); +// newGeneTrack.setAttributeValue(Globals.TRACK_DATA_TYPE_ATTRIBUTE, newGeneTrack.getTrackType().toString()); +// panel.addTrack(newGeneTrack); +// } } /** diff --git a/src/main/java/org/broad/igv/ui/panel/TrackPanel.java b/src/main/java/org/broad/igv/ui/panel/TrackPanel.java index f057999eb0..9cde3bb0a7 100644 --- a/src/main/java/org/broad/igv/ui/panel/TrackPanel.java +++ b/src/main/java/org/broad/igv/ui/panel/TrackPanel.java @@ -175,17 +175,6 @@ public List getTracks() { public void clearTracks() { - final Genome currentGenome = GenomeManager.getInstance().getCurrentGenome(); - Set genomeAnnotationTracks = new HashSet<>(); - if (currentGenome != null) { - if (currentGenome.getGeneTrack() != null) genomeAnnotationTracks.add(currentGenome.getGeneTrack()); - if (currentGenome.getAnnotationTracks() != null) { - for (List t : currentGenome.getAnnotationTracks().values()) { - genomeAnnotationTracks.addAll(t); - } - } - } - for (Track t : getTracks()) { t.unload(); }