/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.gui.track.trackTransfer; import java.awt.Image; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.File; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; import edu.yu.einstein.genplay.core.IO.writer.TransferableTrackWriter; import edu.yu.einstein.genplay.core.IO.writer.geneListWriter.GeneListAsBedWriter; import edu.yu.einstein.genplay.core.manager.application.ConfigurationManager; import edu.yu.einstein.genplay.core.manager.project.ProjectManager; import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome; import edu.yu.einstein.genplay.dataStructure.gene.Gene; import edu.yu.einstein.genplay.dataStructure.genomeWindow.GenomeWindow; import edu.yu.einstein.genplay.dataStructure.list.genomeWideList.SCWList.SCWList; import edu.yu.einstein.genplay.dataStructure.list.genomeWideList.geneList.GeneList; import edu.yu.einstein.genplay.dataStructure.list.listView.ListView; import edu.yu.einstein.genplay.dataStructure.scoredChromosomeWindow.ScoredChromosomeWindow; import edu.yu.einstein.genplay.exception.ExceptionManager; import edu.yu.einstein.genplay.exception.exceptions.IncompatibleAssembliesException; import edu.yu.einstein.genplay.gui.action.track.TASaveAsImage; import edu.yu.einstein.genplay.gui.fileFilter.GenPlayTrackFilter; import edu.yu.einstein.genplay.gui.track.Track; import edu.yu.einstein.genplay.gui.track.layer.Layer; import edu.yu.einstein.genplay.util.Utils; import edu.yu.einstein.genplay.util.ListView.ChromosomeWindowListViews; /** * Transferable track singleton for clipboard, cut and paste * @author Julien Lajugie */ public class TransferableTrack implements Transferable { /** Max element to put in the clipboard for the text flavor containing the data of a track */ private static final int MAX_ELEMENT_TO_PUT_IN_CLIPBOARD = 1000; /** Tranferable rack data flavor */ public transient static final DataFlavor TRACK_FLAVOR = new DataFlavor(TransferableTrack.class, "GenPlay Track"); /** URI list flavor*/ public transient static DataFlavor uriListFlavor = null; // create the flavor static { try { uriListFlavor = new DataFlavor("text/uri-list;class=java.lang.String"); } catch (ClassNotFoundException e){} // can't happen } private static TransferableTrack instance = null; // singleton instance /** Data flavors supported by {@link TransferableTrack} */ public static final DataFlavor[] TRANSFERABLE_TRACK_FLAVORS = { DataFlavor.imageFlavor, DataFlavor.javaFileListFlavor, DataFlavor.stringFlavor, TRACK_FLAVOR, uriListFlavor }; /** * @param transferable a {@link Transferable} * @return Retrieve a file from the transferable if possible */ public static File getFileFromTransferable(Transferable transferable) { try { try { // try to retrieve javaFileListFlavor data @SuppressWarnings("unchecked") List<File> fileList = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor); if (!fileList.isEmpty()) { return fileList.get(0); } } catch (UnsupportedFlavorException e) {} try { // try to retrieve uriListFlavor data String fileNameList = (String) transferable.getTransferData(TransferableTrack.uriListFlavor); return new File(new URI(fileNameList.split("\r\n")[0])); } catch (UnsupportedFlavorException e) {} } catch (Exception e) {} // cannot retrieve data, return null return null; } /** * @return an instance of a {@link ConfigurationManager}. * Makes sure that there is only one unique instance as specified in the singleton pattern */ public static TransferableTrack getInstance() { if (instance == null) { synchronized(TransferableTrack.class) { if (instance == null) { instance = new TransferableTrack(); } } } return instance; } /** * @param transferable * @return a track from the specified transferable. Null if it cannot be extracted. * @throws IOException * @throws IncompatibleAssembliesException */ public static Track getTrackFromTransferable(Transferable transferable) throws IOException, IncompatibleAssembliesException { try { TrackForTransfer transTrack = (TrackForTransfer) transferable.getTransferData(TRACK_FLAVOR); return transTrack.getTrackForTransfer(); } catch (UnsupportedFlavorException e) { return null; } } private Image trackImage; // image for the paste of the track as image private TrackForTransfer track; // track to transfer /** * Creates an instance of {@link TransferableTrack} * @param trackToTransfer {@link Track} to transfer */ private TransferableTrack() {} /** * @return a File List containing one file with the {@link TransferableTrack} serialized */ private List<File> getDataAsFileList() { ObjectOutputStream oos = null; try { String projectName = ProjectManager.getInstance().getProjectName(); File tmpFile = new File(Utils.getTmpDirectoryPath(), projectName + "-" + track.getTrackForTransfer().getName() + "." + GenPlayTrackFilter.EXTENSIONS[0]); TransferableTrackWriter writer = new TransferableTrackWriter(track.getTrackForTransfer(), tmpFile); writer.write(); List<File> fileList = new ArrayList<File>(); fileList.add(tmpFile); return fileList; } catch (Exception e) { ExceptionManager.getInstance().caughtException(Thread.currentThread(), e); } finally { try { if (oos != null) { oos.close(); } } catch (IOException e) {} } return null; } private String getTrackDataAsString() { Layer<?> activeLayer = track.getTrackForTransfer().getActiveLayer(); if (activeLayer != null) { Object data = activeLayer.getData(); GenomeWindow displayedWindow = ProjectManager.getInstance().getProjectWindow().getGenomeWindow(); Chromosome chr = displayedWindow.getChromosome(); if (data instanceof SCWList) { SCWList scwList = (SCWList) data; ListView<ScoredChromosomeWindow> chrLV = ChromosomeWindowListViews.subList(scwList, displayedWindow); String clipboardString = "Layer: " + activeLayer.getName() + ", Region: " + displayedWindow + "\n"; int i =0; for (i = 0; (i < chrLV.size()) && (i < MAX_ELEMENT_TO_PUT_IN_CLIPBOARD); i++) { if (chrLV.get(i).getScore() != 0) { clipboardString += chr + "\t" + chrLV.get(i).getStart() + "\t" + chrLV.get(i).getStop() + "\t" + chrLV.get(i).getScore() + "\n"; } } if (i >= MAX_ELEMENT_TO_PUT_IN_CLIPBOARD) { clipboardString += "and more...\n"; } return clipboardString; } else if (data instanceof GeneList) { GeneList scwList = (GeneList) data; ListView<Gene> chrLV = ChromosomeWindowListViews.subList(scwList, displayedWindow); String clipboardString = "Layer: " + activeLayer.getName() + ", Region: " + displayedWindow + "\n"; int i = 0; for (i = 0; (i < chrLV.size()) && (i < MAX_ELEMENT_TO_PUT_IN_CLIPBOARD); i++) { clipboardString += GeneListAsBedWriter.geneToString(chrLV.get(i), chr) + "\n"; } if (i >= MAX_ELEMENT_TO_PUT_IN_CLIPBOARD) { clipboardString += "and more...\n"; } return clipboardString; } } return null; } /** * @return the track to transfer */ public Track getTrackToTransfer() { return track.getTrackForTransfer(); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor.equals(DataFlavor.javaFileListFlavor)) { return getDataAsFileList(); } if (flavor.equals(DataFlavor.imageFlavor)) { if (trackImage == null) { return TASaveAsImage.createImage(track.getTrackForTransfer()); } else { return trackImage; } } if (flavor.equals(DataFlavor.stringFlavor)) { String dataString = getTrackDataAsString(); if (dataString != null) { // if we can retrieve the data from the active layer we put it in the clipboard return dataString; } else { // otherwise the default is the track name return track.getTrackForTransfer().getName(); } } if (flavor.equals(TRACK_FLAVOR)) { return track; } if (flavor.equals(uriListFlavor)) { List<File> fileList = getDataAsFileList(); if (fileList == null) { return null; } if (flavor.isRepresentationClassInputStream() || flavor.getRepresentationClass().equals(String.class)) { return fileList.get(0).toURI() + "\r\n"; } else { return fileList; } } return null; } @Override public DataFlavor[] getTransferDataFlavors() { return TRANSFERABLE_TRACK_FLAVORS; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { for(DataFlavor supportedFlavor: TRANSFERABLE_TRACK_FLAVORS) { if (flavor.match(supportedFlavor)) { return true; } } return false; } /** * Set the image of the track that needs to be transfered. * Used if the track is paste as an image. * If the image is not specified before transfer the transferable * will try to generate an image. This can result in an error if * the track is not visible * @param trackImage image of the track to be transfered */ public void setImageToTransfer(Image trackImage) { this.trackImage = trackImage; } /** * Sets the track that need to be transfered * @param trackToTransfer {@link Track} to transfer */ public void setTrackToTransfer(Track trackToTransfer) { track = new TrackForTransfer(trackToTransfer); } }