/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jspresso 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.view.swing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; /** * Strongly inspired from javax.swing.plaf.basic.BasicTransferable. * * @author Vincent Vandenschrick */ public class BasicTransferable implements Transferable, ClipboardOwner { private static final Logger LOG = LoggerFactory.getLogger(BasicTransferable.class); private final String plainData; private final String htmlData; private static final DataFlavor[] HTML_FLAVORS; private static final DataFlavor[] STRING_FLAVORS; private static final DataFlavor[] PLAIN_FLAVORS; static { HTML_FLAVORS = new DataFlavor[3]; PLAIN_FLAVORS = new DataFlavor[3]; STRING_FLAVORS = new DataFlavor[2]; try { HTML_FLAVORS[0] = new DataFlavor("text/html;class=java.lang.String"); HTML_FLAVORS[1] = new DataFlavor("text/html;class=java.io.Reader"); HTML_FLAVORS[2] = new DataFlavor( "text/html;charset=unicode;class=java.io.InputStream"); PLAIN_FLAVORS[0] = new DataFlavor("text/plain;class=java.lang.String"); PLAIN_FLAVORS[1] = new DataFlavor("text/plain;class=java.io.Reader"); PLAIN_FLAVORS[2] = new DataFlavor( "text/plain;charset=unicode;class=java.io.InputStream"); STRING_FLAVORS[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=java.lang.String"); STRING_FLAVORS[1] = DataFlavor.stringFlavor; } catch (ClassNotFoundException cle) { LOG.error("Error initializing javax.swing.plaf.basic.BasicTransferable", cle); } } /** * Constructs a new {@code BasicTransferable} instance. * * @param plainData * plain data. * @param htmlData * HTML data. */ public BasicTransferable(String plainData, String htmlData) { this.plainData = plainData; this.htmlData = htmlData; } /** * Returns an array of DataFlavor objects indicating the flavors the data can * be provided in. The array should be ordered according to preference for * providing the data (from most richly descriptive to least descriptive). * * @return an array of data flavors in which this data can be transferred */ @Override public DataFlavor[] getTransferDataFlavors() { DataFlavor[] richerFlavors = getRicherFlavors(); int nRicher = 0; if (richerFlavors != null) { nRicher = richerFlavors.length; } int nHTML = 0; if (isHTMLSupported()) { nHTML = HTML_FLAVORS.length; } int nPlain = 0; if (isPlainSupported()) { nPlain = PLAIN_FLAVORS.length; } int nString = 0; if (isPlainSupported()) { nString = STRING_FLAVORS.length; } int nFlavors = nRicher + nHTML + nPlain + nString; DataFlavor[] flavors = new DataFlavor[nFlavors]; // fill in the array int nDone = 0; if (nRicher > 0) { System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); nDone += nRicher; } if (nHTML > 0) { System.arraycopy(HTML_FLAVORS, 0, flavors, nDone, nHTML); nDone += nHTML; } if (nPlain > 0) { System.arraycopy(PLAIN_FLAVORS, 0, flavors, nDone, nPlain); nDone += nPlain; } if (nString > 0) { System.arraycopy(STRING_FLAVORS, 0, flavors, nDone, nString); nDone += nString; } return flavors; } /** * Returns whether or not the specified data flavor is supported for this * object. * * @param flavor * the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ @Override public boolean isDataFlavorSupported(DataFlavor flavor) { DataFlavor[] flavors = getTransferDataFlavors(); for (DataFlavor flavor1 : flavors) { if (flavor1.equals(flavor)) { return true; } } return false; } /** * Returns an object which represents the data to be transferred. The class of * the object returned is defined by the representation class of the flavor. * * @param flavor * the requested flavor for the data * @return the transferred object. * * @throws IOException * if the data is no longer available in the requested flavor. * @throws UnsupportedFlavorException * if the requested data flavor is not supported. * @see DataFlavor#getRepresentationClass */ @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (isRicherFlavor(flavor)) { return getRicherData(flavor); } if (isHTMLFlavor(flavor)) { String data = getHTMLData(); if (data == null) { data = ""; } if (String.class.equals(flavor.getRepresentationClass())) { return data; } else if (Reader.class.equals(flavor.getRepresentationClass())) { return new StringReader(data); } else if (InputStream.class.equals(flavor.getRepresentationClass())) { return new ByteArrayInputStream(data.getBytes()); } // fall through to unsupported } else if (isPlainFlavor(flavor)) { String data = getPlainData(); if (data == null) { data = ""; } if (String.class.equals(flavor.getRepresentationClass())) { return data; } else if (Reader.class.equals(flavor.getRepresentationClass())) { return new StringReader(data); } else if (InputStream.class.equals(flavor.getRepresentationClass())) { return new ByteArrayInputStream(data.getBytes()); } // fall through to unsupported } else if (isStringFlavor(flavor)) { String data = getPlainData(); if (data == null) { data = ""; } return data; } throw new UnsupportedFlavorException(flavor); } // --- richer subclass flavors ---------------------------------------------- /** * Tests whether the passed in flavor is richer than this. * * @param flavor * the flavor to test. * @return @{code true} if the flavor is richer than this. */ protected boolean isRicherFlavor(DataFlavor flavor) { DataFlavor[] richerFlavors = getRicherFlavors(); if (richerFlavors != null) { for (DataFlavor richerFlavor : richerFlavors) { if (richerFlavor.equals(flavor)) { return true; } } } return false; } /** * Some subclasses will have flavors that are more descriptive than HTML or * plain text. If this method returns a non-null value, it will be placed at * the start of the array of supported flavors. * * @return the richer compatible flavors. */ protected DataFlavor[] getRicherFlavors() { return null; } /** * Gets richer data. * * @param flavor * the flavor to get the data. * @throws UnsupportedFlavorException * whenever this flavor is not supported. * @return the richer data. */ @SuppressWarnings({"unused", "UnusedParameters"}) protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { return null; } // --- html flavors ---------------------------------------------------------- /** * Returns whether or not the specified data flavor is an HTML flavor that is * supported. * * @param flavor * the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isHTMLFlavor(DataFlavor flavor) { DataFlavor[] flavors = HTML_FLAVORS; for (DataFlavor flavor1 : flavors) { if (flavor1.equals(flavor)) { return true; } } return false; } /** * Should the HTML flavors be offered? If so, the method getHTMLData should be * implemented to provide something reasonable. * * @return true if HTML is supported. */ protected boolean isHTMLSupported() { return htmlData != null; } /** * Fetch the data in a text/html format. * * @return HTML data. */ protected String getHTMLData() { return htmlData; } // --- plain text flavors ---------------------------------------------------- /** * Returns whether or not the specified data flavor is an plain flavor that is * supported. * * @param flavor * the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isPlainFlavor(DataFlavor flavor) { DataFlavor[] flavors = PLAIN_FLAVORS; for (DataFlavor flavor1 : flavors) { if (flavor1.equals(flavor)) { return true; } } return false; } /** * Should the plain text flavors be offered? If so, the method getPlainData * should be implemented to provide something reasonable. * * @return true if plain flavor is supported. */ protected boolean isPlainSupported() { return plainData != null; } /** * Fetch the data in a text/plain format. * * @return plain data. */ protected String getPlainData() { return plainData; } // --- string flavors // -------------------------------------------------------- /** * Returns whether or not the specified data flavor is a String flavor that is * supported. * * @param flavor * the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isStringFlavor(DataFlavor flavor) { DataFlavor[] flavors = STRING_FLAVORS; for (DataFlavor flavor1 : flavors) { if (flavor1.equals(flavor)) { return true; } } return false; } /** * {@inheritDoc} */ @Override public void lostOwnership(Clipboard clipboard, Transferable contents) { // Don't do anything } }