/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Michael Danilov
* @version $Revision$
*/
package internal.java.awt.datatransfer;
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.harmony.awt.internal.nls.Messages;
public class DataFlavor implements Externalizable, Cloneable {
private static final long serialVersionUID = 8367026044764648243L;
/**
* @deprecated
*/
@Deprecated
public static final DataFlavor plainTextFlavor =
new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream", //$NON-NLS-1$
"Plain Text"); //$NON-NLS-1$
public static final DataFlavor stringFlavor =
new DataFlavor("application/x-java-serialized-object; class=java.lang.String", //$NON-NLS-1$
"Unicode String"); //$NON-NLS-1$
/* public static final DataFlavor imageFlavor =
new DataFlavor("image/x-java-image; class=java.awt.Image", //$NON-NLS-1$
"Image"); //$NON-NLS-1$
*/
public static final DataFlavor javaFileListFlavor =
new DataFlavor("application/x-java-file-list; class=java.util.List", //$NON-NLS-1$
"application/x-java-file-list"); //$NON-NLS-1$
public static final String javaJVMLocalObjectMimeType =
"application/x-java-jvm-local-objectref"; //$NON-NLS-1$
public static final String javaRemoteObjectMimeType =
"application/x-java-remote-object"; //$NON-NLS-1$
public static final String javaSerializedObjectMimeType =
"application/x-java-serialized-object"; //$NON-NLS-1$
private static final String sortedTextFlavors[] = {
"text/sgml", //$NON-NLS-1$
"text/xml", //$NON-NLS-1$
"text/html", //$NON-NLS-1$
"text/rtf", //$NON-NLS-1$
"text/enriched", //$NON-NLS-1$
"text/richtext", //$NON-NLS-1$
"text/uri-list", //$NON-NLS-1$
"text/tab-separated-values", //$NON-NLS-1$
"text/t140" , //$NON-NLS-1$
"text/rfc822-headers", //$NON-NLS-1$
"text/parityfec", //$NON-NLS-1$
"text/directory", //$NON-NLS-1$
"text/css", //$NON-NLS-1$
"text/calendar", //$NON-NLS-1$
"application/x-java-serialized-object", //$NON-NLS-1$
"text/plain" //$NON-NLS-1$
};
private static DataFlavor plainUnicodeFlavor = null;
private String humanPresentableName;
private Class<?> representationClass;
private MimeTypeProcessor.MimeType mimeInfo;
public static String getDefaultCharset() {
return "unicode"; //$NON-NLS-1$
}
public static final DataFlavor getTextPlainUnicodeFlavor() {
if (plainUnicodeFlavor == null) {
plainUnicodeFlavor = new DataFlavor("text/plain" //$NON-NLS-1$
+ "; charset=" + getDefaultCharset() //$NON-NLS-1$
+ "; class=java.io.InputStream", //$NON-NLS-1$
"Plain Text"); //$NON-NLS-1$
}
return plainUnicodeFlavor;
}
protected static final Class<?> tryToLoadClass(String className, ClassLoader fallback)
throws ClassNotFoundException
{
try {
return Class.forName(className);
} catch (ClassNotFoundException e0) {
try {
return ClassLoader.getSystemClassLoader().loadClass(className);
} catch (ClassNotFoundException e1) {
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
if (contextLoader != null) {
try {
return contextLoader.loadClass(className);
} catch (ClassNotFoundException e2) {
}
}
return fallback.loadClass(className);
}
}
}
private static boolean isCharsetSupported(String charset) {
try {
return Charset.isSupported(charset);
} catch (IllegalCharsetNameException e) {
return false;
}
}
public DataFlavor() {
mimeInfo = null;
humanPresentableName = null;
representationClass = null;
}
public DataFlavor(Class<?> representationClass, String humanPresentableName) {
mimeInfo = new MimeTypeProcessor.MimeType("application", "x-java-serialized-object"); //$NON-NLS-1$ //$NON-NLS-2$
if (humanPresentableName != null) {
this.humanPresentableName = humanPresentableName;
} else {
this.humanPresentableName = "application/x-java-serialized-object"; //$NON-NLS-1$
}
mimeInfo.addParameter("class", representationClass.getName()); //$NON-NLS-1$
this.representationClass = representationClass;
}
public DataFlavor(String mimeType, String humanPresentableName) {
try {
init(mimeType, humanPresentableName, null);
} catch (ClassNotFoundException e) {
// awt.16C=Can't load class: {0}
throw new IllegalArgumentException(Messages.getString("awt.16C", mimeInfo.getParameter("class")),e); //$NON-NLS-1$//$NON-NLS-2$
}
}
public DataFlavor(String mimeType) throws ClassNotFoundException {
init(mimeType, null, null);
}
public DataFlavor(String mimeType, String humanPresentableName, ClassLoader classLoader)
throws ClassNotFoundException
{
init(mimeType, humanPresentableName, classLoader);
}
private void init(String mimeType, String humanPresentableName, ClassLoader classLoader)
throws ClassNotFoundException
{
String className;
try {
mimeInfo = MimeTypeProcessor.parse(mimeType);
} catch (IllegalArgumentException e) {
// awt.16D=Can't parse MIME type: {0}
throw new IllegalArgumentException(Messages.getString("awt.16D", mimeType)); //$NON-NLS-1$
}
if (humanPresentableName != null) {
this.humanPresentableName = humanPresentableName;
} else {
this.humanPresentableName = mimeInfo.getPrimaryType() + '/' + mimeInfo.getSubType();
}
className = mimeInfo.getParameter("class"); //$NON-NLS-1$
if (className == null) {
className = "java.io.InputStream"; //$NON-NLS-1$
mimeInfo.addParameter("class", className); //$NON-NLS-1$
}
representationClass = (classLoader == null) ?
Class.forName(className) :
classLoader.loadClass(className);
}
private String getCharset() {
if ((mimeInfo == null) || isCharsetRedundant()) {
return ""; //$NON-NLS-1$
}
String charset = mimeInfo.getParameter("charset"); //$NON-NLS-1$
if (isCharsetRequired() && ((charset == null) || (charset.length() == 0))) {
return getDefaultCharset();
}
if (charset == null) {
return ""; //$NON-NLS-1$
}
return charset;
}
private boolean isCharsetRequired() {
String type = mimeInfo.getFullType();
return (type.equals("text/sgml") || //$NON-NLS-1$
type.equals("text/xml") || //$NON-NLS-1$
type.equals("text/html") || //$NON-NLS-1$
type.equals("text/enriched") || //$NON-NLS-1$
type.equals("text/richtext") || //$NON-NLS-1$
type.equals("text/uri-list") || //$NON-NLS-1$
type.equals("text/directory") || //$NON-NLS-1$
type.equals("text/css") || //$NON-NLS-1$
type.equals("text/calendar") || //$NON-NLS-1$
type.equals("application/x-java-serialized-object") || //$NON-NLS-1$
type.equals("text/plain")); //$NON-NLS-1$
}
private boolean isCharsetRedundant() {
String type = mimeInfo.getFullType();
return (type.equals("text/rtf") || //$NON-NLS-1$
type.equals("text/tab-separated-values") || //$NON-NLS-1$
type.equals("text/t140") || //$NON-NLS-1$
type.equals("text/rfc822-headers") || //$NON-NLS-1$
type.equals("text/parityfec")); //$NON-NLS-1$
}
MimeTypeProcessor.MimeType getMimeInfo() {
return mimeInfo;
}
public String getPrimaryType() {
return (mimeInfo != null) ? mimeInfo.getPrimaryType() : null;
}
public String getSubType() {
return (mimeInfo != null) ? mimeInfo.getSubType() : null;
}
public String getMimeType() {
return (mimeInfo != null) ? MimeTypeProcessor.assemble(mimeInfo) : null;
}
public String getParameter(String paramName) {
String lowerName = paramName.toLowerCase();
if (lowerName.equals("humanpresentablename")) { //$NON-NLS-1$
return humanPresentableName;
}
return mimeInfo != null ? mimeInfo.getParameter(lowerName) : null;
}
public String getHumanPresentableName() {
return humanPresentableName;
}
public void setHumanPresentableName(String humanPresentableName) {
this.humanPresentableName = humanPresentableName;
}
public Class<?> getRepresentationClass() {
return representationClass;
}
public final Class<?> getDefaultRepresentationClass() {
return InputStream.class;
}
public final String getDefaultRepresentationClassAsString() {
return getDefaultRepresentationClass().getName();
}
public boolean isRepresentationClassSerializable() {
return Serializable.class.isAssignableFrom(representationClass);
}
public boolean isRepresentationClassRemote() {
// Code should be enabled when RMI is supported
// return java.rmi.Remote.class.isAssignableFrom(representationClass);
return false;
}
public boolean isRepresentationClassReader() {
return Reader.class.isAssignableFrom(representationClass);
}
public boolean isRepresentationClassInputStream() {
return InputStream.class.isAssignableFrom(representationClass);
}
public boolean isRepresentationClassCharBuffer() {
return CharBuffer.class.isAssignableFrom(representationClass);
}
public boolean isRepresentationClassByteBuffer() {
return ByteBuffer.class.isAssignableFrom(representationClass);
}
/**
* @deprecated
*/
@Deprecated
protected String normalizeMimeTypeParameter(String parameterName, String parameterValue) {
return parameterValue;
}
/**
* @deprecated
*/
@Deprecated
protected String normalizeMimeType(String mimeType) {
return mimeType;
}
public final boolean isMimeTypeEqual(DataFlavor dataFlavor) {
return mimeInfo != null ? mimeInfo.equals(dataFlavor.mimeInfo)
: (dataFlavor.mimeInfo == null);
}
public boolean isMimeTypeEqual(String mimeType) {
try {
return mimeInfo.equals(MimeTypeProcessor.parse(mimeType));
} catch (IllegalArgumentException e) {
return false;
}
}
public synchronized void writeExternal(ObjectOutput os) throws IOException {
os.writeObject(humanPresentableName);
os.writeObject(mimeInfo);
}
public synchronized void readExternal(ObjectInput is)
throws IOException, ClassNotFoundException
{
humanPresentableName = (String) is.readObject();
mimeInfo = (MimeTypeProcessor.MimeType) is.readObject();
representationClass = (mimeInfo != null) ?
Class.forName(mimeInfo.getParameter("class")) : null; //$NON-NLS-1$
}
@Override
public Object clone() throws CloneNotSupportedException {
DataFlavor clone = new DataFlavor();
clone.humanPresentableName = humanPresentableName;
clone.representationClass = representationClass;
clone.mimeInfo = (mimeInfo != null) ? (MimeTypeProcessor.MimeType)
mimeInfo.clone() : null;
return clone;
}
@Override
public String toString() {
/* The format is based on 1.5 release behavior
* which can be revealed by the following code:
*
* System.out.println(DataFlavor.imageFlavor.toString());
*/
return (getClass().getName()
+ "[MimeType=(" + getMimeType() //$NON-NLS-1$
+ ");humanPresentableName=" + humanPresentableName + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
public boolean isMimeTypeSerializedObject() {
return isMimeTypeEqual(javaSerializedObjectMimeType);
}
@Override
public boolean equals(Object o) {
if ((o == null) || !(o instanceof DataFlavor)) {
return false;
}
return equals((DataFlavor) o);
}
public boolean equals(DataFlavor that) {
if (that == this) {
return true;
}
if (that == null) {
return false;
}
if (mimeInfo == null) {
return (that.mimeInfo == null);
}
if (!(mimeInfo.equals(that.mimeInfo) &&
representationClass.equals(that.representationClass)))
{
return false;
}
if (!mimeInfo.getPrimaryType().equals("text") || isUnicodeFlavor()) { //$NON-NLS-1$
return true;
}
String charset1 = getCharset();
String charset2 = that.getCharset();
if (!isCharsetSupported(charset1) || !isCharsetSupported(charset2)) {
return charset1.equalsIgnoreCase(charset2);
}
return (Charset.forName(charset1).equals(Charset.forName(charset2)));
}
@Deprecated
public boolean equals(String s) {
if (s == null) {
return false;
}
return isMimeTypeEqual(s);
}
public boolean match(DataFlavor that) {
return equals(that);
}
@Override
public int hashCode() {
return getKeyInfo().hashCode();
}
private String getKeyInfo() {
String key = mimeInfo.getFullType() + ";class=" + representationClass.getName(); //$NON-NLS-1$
if (!mimeInfo.getPrimaryType().equals("text") || isUnicodeFlavor()) { //$NON-NLS-1$
return key;
}
return key + ";charset=" + getCharset().toLowerCase(); //$NON-NLS-1$
}
public boolean isFlavorSerializedObjectType() {
return (isMimeTypeSerializedObject()
&& isRepresentationClassSerializable());
}
public boolean isFlavorRemoteObjectType() {
return (isMimeTypeEqual(javaRemoteObjectMimeType)
&& isRepresentationClassRemote());
}
public boolean isFlavorJavaFileListType() {
return (java.util.List.class.isAssignableFrom(representationClass) &&
isMimeTypeEqual(javaFileListFlavor));
}
public boolean isFlavorTextType() {
if (equals(stringFlavor) || equals(plainTextFlavor)) {
return true;
}
if ((mimeInfo != null) && !mimeInfo.getPrimaryType().equals("text")) { //$NON-NLS-1$
return false;
}
String charset = getCharset();
if (isByteCodeFlavor()) {
if (charset.length() != 0) {
return isCharsetSupported(charset);
}
return true;
}
return isUnicodeFlavor();
}
public Reader getReaderForText(Transferable transferable)
throws UnsupportedFlavorException, IOException
{
Object data = transferable.getTransferData(this);
if (data == null) {
// awt.16E=Transferable has null data
throw new IllegalArgumentException(Messages.getString("awt.16E")); //$NON-NLS-1$
}
if (data instanceof Reader) {
Reader reader = (Reader) data;
reader.reset();
return reader;
} else if (data instanceof String) {
return new StringReader((String) data);
} else if (data instanceof CharBuffer) {
return new CharArrayReader(((CharBuffer) data).array());
} else if (data instanceof char[]) {
return new CharArrayReader((char[]) data);
} else {
String charset = getCharset();
InputStream stream;
if (data instanceof InputStream) {
stream = (InputStream) data;
stream.reset();
} else if (data instanceof ByteBuffer) {
stream = new ByteArrayInputStream((((ByteBuffer) data).array()));
} else if (data instanceof byte[]) {
stream = new ByteArrayInputStream((byte[]) data);
} else {
// awt.16F=Can't create reader for this representation class
throw new IllegalArgumentException(Messages.getString("awt.16F")); //$NON-NLS-1$
}
if (charset.length() == 0) {
return new InputStreamReader(stream);
}
return new InputStreamReader(stream, charset);
}
}
public static final DataFlavor selectBestTextFlavor(DataFlavor[] availableFlavors) {
if (availableFlavors == null) {
return null;
}
List<List<DataFlavor>> sorted = sortTextFlavorsByType(new LinkedList<DataFlavor>(Arrays.asList(availableFlavors)));
if (sorted.isEmpty()) {
return null;
}
List<DataFlavor> bestSorted = sorted.get(0);
if (bestSorted.size() == 1) {
return bestSorted.get(0);
}
if (bestSorted.get(0).getCharset().length() == 0) {
return selectBestFlavorWOCharset(bestSorted);
}
return selectBestFlavorWCharset(bestSorted);
}
private static DataFlavor selectBestFlavorWCharset(List<DataFlavor> list) {
List<DataFlavor> best;
best = getFlavors(list, Reader.class);
if (best != null) {
return best.get(0);
}
best = getFlavors(list, String.class);
if (best != null) {
return best.get(0);
}
best = getFlavors(list, CharBuffer.class);
if (best != null) {
return best.get(0);
}
best = getFlavors(list, char[].class);
if (best != null) {
return best.get(0);
}
return selectBestByCharset(list);
}
private static DataFlavor selectBestByCharset(List<DataFlavor> list) {
List<DataFlavor> best;
best = getFlavors(list, new String[] {"UTF-16", "UTF-8", "UTF-16BE", "UTF-16LE"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
if (best == null) {
best = getFlavors(list, new String[] {getDefaultCharset()});
if (best == null) {
best = getFlavors(list, new String[] {"US-ASCII"}); //$NON-NLS-1$
if (best == null) {
best = selectBestByAlphabet(list);
}
}
}
if (best != null) {
if (best.size() == 1) {
return best.get(0);
}
return selectBestFlavorWOCharset(best);
}
return null;
}
private static List<DataFlavor> selectBestByAlphabet(List<DataFlavor> list) {
String charsets[] = new String[list.size()];
LinkedList<DataFlavor> best = new LinkedList<DataFlavor>();
for (int i = 0; i < charsets.length; i++) {
charsets[i] = list.get(i).getCharset();
}
Arrays.sort(charsets, String.CASE_INSENSITIVE_ORDER);
for (DataFlavor flavor : list) {
if (charsets[0].equalsIgnoreCase(flavor.getCharset())) {
best.add(flavor);
}
}
return best.isEmpty() ? null : best;
}
private static List<DataFlavor> getFlavors(List<DataFlavor> list, String[] charset) {
LinkedList<DataFlavor> sublist = new LinkedList<DataFlavor>();
for (Iterator<DataFlavor> i = list.iterator(); i.hasNext();) {
DataFlavor flavor = i.next();
if (isCharsetSupported(flavor.getCharset())) {
for (String element : charset) {
if (Charset.forName(element).equals(Charset.forName(flavor.getCharset()))) {
sublist.add(flavor);
}
}
} else {
i.remove();
}
}
return sublist.isEmpty() ? null : list;
}
private static DataFlavor selectBestFlavorWOCharset(List<DataFlavor> list) {
List<DataFlavor> best;
best = getFlavors(list, InputStream.class);
if (best != null) {
return best.get(0);
}
best = getFlavors(list, ByteBuffer.class);
if (best != null) {
return best.get(0);
}
best = getFlavors(list, byte[].class);
if (best != null) {
return best.get(0);
}
return list.get(0);
}
private static List<DataFlavor> getFlavors(List<DataFlavor> list, Class<?> klass) {
LinkedList<DataFlavor> sublist = new LinkedList<DataFlavor>();
for (DataFlavor flavor : list) {
if (flavor.representationClass.equals(klass)) {
sublist.add(flavor);
}
}
return sublist.isEmpty() ? null : list;
}
private static List<List<DataFlavor>> sortTextFlavorsByType(List<DataFlavor> availableFlavors) {
LinkedList<List<DataFlavor>> list = new LinkedList<List<DataFlavor>>();
for (String element : sortedTextFlavors) {
List<DataFlavor> subList = fetchTextFlavors(availableFlavors, element);
if (subList != null) {
list.addLast(subList);
}
}
if (!availableFlavors.isEmpty()) {
list.addLast(availableFlavors);
}
return list;
}
private static List<DataFlavor> fetchTextFlavors(List<DataFlavor> availableFlavors, String mimeType) {
LinkedList<DataFlavor> list = new LinkedList<DataFlavor>();
for (Iterator<DataFlavor> i = availableFlavors.iterator(); i.hasNext();) {
DataFlavor flavor = i.next();
if (flavor.isFlavorTextType()) {
if (flavor.mimeInfo.getFullType().equals(mimeType)) {
if (!list.contains(flavor)) {
list.add(flavor);
}
i.remove();
}
} else {
i.remove();
}
}
return list.isEmpty() ? null : list;
}
private boolean isUnicodeFlavor() {
return (representationClass != null)
&& (representationClass.equals(Reader.class)
|| representationClass.equals(String.class)
|| representationClass.equals(CharBuffer.class)
|| representationClass.equals(char[].class));
}
private boolean isByteCodeFlavor() {
return (representationClass != null)
&& (representationClass.equals(InputStream.class)
|| representationClass.equals(ByteBuffer.class)
|| representationClass.equals(byte[].class));
}
}