X-Git-Url: https://feistymeow.org/gitweb/?a=blobdiff_plain;f=kona%2Fsrc%2Forg%2Ffeistymeow%2Fdragdrop%2FListTransferable.java;fp=kona%2Fsrc%2Forg%2Ffeistymeow%2Fdragdrop%2FListTransferable.java;h=76b4c361d2d9e6278460861e60d89cfbe1df9c45;hb=67d3d9a5e40685d3898f34a3651944c8a0c3e2ca;hp=0000000000000000000000000000000000000000;hpb=2d9bf2a5bf795aabd3498d96e5b005c18889002d;p=feisty_meow.git diff --git a/kona/src/org/feistymeow/dragdrop/ListTransferable.java b/kona/src/org/feistymeow/dragdrop/ListTransferable.java new file mode 100644 index 00000000..76b4c361 --- /dev/null +++ b/kona/src/org/feistymeow/dragdrop/ListTransferable.java @@ -0,0 +1,311 @@ +package org.feistymeow.dragdrop; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.StringTokenizer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implements a transferable object that understands URI lists as well as java file lists. This is + * useful for implementing file drag and drop that will work across different platforms (such as + * Gnome on Linux). + * + * @author Chris Koeritz + * @copyright Copyright (c) 2012-$now By University of Virginia + * @license This file is free software; you can modify and redistribute it under the terms of the + * Apache License v2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +@SuppressWarnings("serial") +public class ListTransferable extends Vector implements Transferable +{ + static private Log logger = LogFactory.getLog(ListTransferable.class); + + public ListTransferable() + { + } + + public ListTransferable(Object initial) + { + if (initial != null) add(initial); + } + + public ListTransferable(List initial) + { + if (initial != null) addAll(initial); + } + + /** + * create a new flavor. this one understands URI lists, such as: file:///home/fred/arf.txt\r\n + * file:///etc/inputrc\r\n http://gruntose.com\r\n ... + */ + private static DataFlavor URIListFlavor; + static { + try { + URIListFlavor = new DataFlavor("text/uri-list;class=java.lang.String"); + } catch (ClassNotFoundException e) { + logger.error("should never happen", e); + } + } + private static DataFlavor AltURIListFlavor; + static { + try { + AltURIListFlavor = new DataFlavor("text/uri-list;representationclass=java.lang.String"); + } catch (ClassNotFoundException e) { + logger.error("should never happen", e); + } + } + + /** + * accessors for our special featured flavors of URI lists. + */ + public static DataFlavor getURIListFlavor1() + { + return URIListFlavor; + } + + public static DataFlavor getURIListFlavor2() + { + return AltURIListFlavor; + } + + /** + * register the types of transfers that we understand. this is really only the normal java file + * list and our new URI list. + */ + protected ArrayList FLAVORS = new ArrayList(Arrays.asList( + DataFlavor.javaFileListFlavor, URIListFlavor, AltURIListFlavor)); + + /** + * a function that must be overridden by derived classes if they are not initially seeding the + * vector of objects that we hold. the caller of this function expects it will populate the + * vector held here with usable objects. + */ + public boolean loadDataJustInTime(DataFlavor flavor) + { + logger.warn("base loadDataJustInTime. derived class should have implemented this."); + return false; + } + + /** + * using the set of files that we've been handed, we can do transfers using our two supported + * flavors. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, + java.io.IOException + { + if (flavor == null) return null; + if (size() == 0) { + logger.debug("size was zero, so loading data just in time"); + boolean worked = loadDataJustInTime(flavor); + if (!worked || (size() == 0)) { + logger.warn("failed to retrieve data just in time for getTransferData."); + return null; + } + } + // help from workaround at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4899516 + logger.debug("responding to flavor: " + flavor.toString()); + if (flavor.equals(DataFlavor.javaFileListFlavor)) { + logger.debug("java file list flavor..."); + List data = new java.util.ArrayList(); + data.addAll(this); + return data; + } else if (flavor.equals(URIListFlavor) || flavor.equals(AltURIListFlavor)) { + logger.debug("uri list flavor..."); + StringBuilder data = new StringBuilder(); + Iterator iter = iterator(); + while (iter.hasNext()) { + Object x = iter.next(); + if (x instanceof File) { + File elem = (File) x; + data.append(elem.toURI() + "\r\n"); + } else if (x instanceof String) { + data.append((String) x + "\r\n"); + } else { + logger.debug("did not know how to handle type in transfer: " + x.toString()); + } + } + logger.debug("returning URI string: " + data.toString()); + return data.toString(); + } else { + logger.debug("getTransferData: didn't know how to handle the requested flavor."); + throw new UnsupportedFlavorException(flavor); + } + } + + /** + * returns the list of all transfer flavors we understand. + */ + public DataFlavor[] getTransferDataFlavors() + { + return (DataFlavor[]) FLAVORS.toArray(new DataFlavor[FLAVORS.size()]); + } + + /** + * reports if a particular flavor is handled here. + */ + public boolean isDataFlavorSupported(DataFlavor flavor) + { + if (flavor == null) return false; + for (int i = 0; i < FLAVORS.size(); i++) { + if (flavor.equals((DataFlavor) FLAVORS.get(i))) { + return true; + } + } + logger.debug("failed to find flavor: " + flavor.toString()); + return false; + } + + /** + * a helper method that can process transfer data from either a java file list or a URI list. + */ + @SuppressWarnings("unchecked") + static public List extractData(Transferable tran) { + if (tran == null) return null; + if (tran.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + logger.debug("extractData seeing java files flavor."); + try { + return (List) tran.getTransferData(DataFlavor.javaFileListFlavor); + } catch (Throwable cause) { + logger.error("extractData caught exception for java file list.", cause); + return null; + } + } else if (tran.isDataFlavorSupported(ListTransferable.getURIListFlavor1()) + || tran.isDataFlavorSupported(ListTransferable.getURIListFlavor2())) { + logger.debug("extractData seeing uri list flavor."); + try { + return textURIListToFileList((String) tran.getTransferData(getURIListFlavor1())); + } catch (Throwable cause) { + logger.error("extractData caught exception for URI list.", cause); + return null; + } + } + logger.error("extractData: Transferable did not support known data flavor."); + return null; + } + + /** + * translates the string in "data" into a list of Files. + * + * @param data + * a string formatted with possibly multiple URIs separated by CRLF. + * @return a list of the files as java File objects. many thanks to + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4899516 + */ + public static List textURIListToFileList(String data) + { + if (data == null) return null; + List list = new ArrayList(0); + for (StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();) { + String s = st.nextToken(); + if (s.startsWith("#")) { + // the line is a comment (as per the RFC 2483) + continue; + } + try { + java.net.URI uri = new java.net.URI(s); + java.io.File file = new java.io.File(uri); + list.add(file); + } catch (java.net.URISyntaxException e) { + // this is a malformed URI. + logger.error("Found a malformed URI of: " + data); + } catch (IllegalArgumentException e) { + // the URI is not a valid 'file:' URI + logger.error("Found invalid 'file:' URI of: " + data); + } + } + return list; + } + + /** + * This function will retrieve the file list from a standard file list flavor. + */ + @SuppressWarnings("unchecked") + public static List processStandardFileList(Transferable tran) + { + if (tran == null) return null; + logger.debug("trying java file list flavor."); + try { + return (List) tran.getTransferData(DataFlavor.javaFileListFlavor); + } catch (Throwable cause) { + logger.debug("failed to retrieve transfer data for standard java file list flavor.", + cause); + } + return new ArrayList(); + } + + /** + * checks if the transferable is appropriate to try to use as a java Reader. + */ + public static boolean checkReaderFlavor(Transferable tran) + { + if (tran == null) return false; + DataFlavor[] flavors = tran.getTransferDataFlavors(); + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].isRepresentationClassReader()) + return true; + } + return false; + } + + /** + * Use a Reader to handle an incoming transferable. + */ + public static List processReaderFlavor(Transferable tran) + { + if (tran == null) return null; + logger.debug("trying URI list flavor."); + DataFlavor[] flavors = tran.getTransferDataFlavors(); + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].isRepresentationClassReader()) { + // it looks like we can work with this flavor just fine. + logger.debug("found a reader flavor."); + try { + Reader reader = flavors[i].getReaderForText(tran); + BufferedReader br = new BufferedReader(reader); + return createFileArray(br); + } catch (Throwable cause) { + logger.debug("failed to scan reader for file list."); + } + } + } + return new ArrayList(); + } + + private static String ZERO_CHAR_STRING = "" + (char) 0; + + public static List createFileArray(BufferedReader bReader) { + if (bReader == null) return null; + try { + List list = new ArrayList(); + String line = null; + while ((line = bReader.readLine()) != null) { + try { + // kde seems to append a 0 char to the end of the reader + if (ZERO_CHAR_STRING.equals(line)) + continue; + File file = new java.io.File(new java.net.URI(line)); + list.add(file); + } catch (Exception ex) { + logger.error("Error with " + line + ": " + ex.getMessage()); + } + } + + return list; + } catch (IOException ex) { + logger.error("IOException while working on file list"); + } + return new ArrayList(); + } + +}