0N/A/*
2362N/A * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.awt.datatransfer;
0N/A
0N/Aimport java.awt.Toolkit;
0N/A
0N/Aimport java.lang.ref.SoftReference;
0N/A
0N/Aimport java.io.BufferedReader;
0N/Aimport java.io.File;
0N/Aimport java.io.InputStreamReader;
0N/Aimport java.io.IOException;
0N/A
0N/Aimport java.net.URL;
0N/Aimport java.net.MalformedURLException;
0N/A
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.LinkedList;
0N/Aimport java.util.List;
0N/Aimport java.util.Map;
0N/Aimport java.util.Set;
0N/Aimport java.util.WeakHashMap;
0N/A
0N/Aimport sun.awt.datatransfer.DataTransferer;
0N/A
0N/A/**
0N/A * The SystemFlavorMap is a configurable map between "natives" (Strings), which
0N/A * correspond to platform-specific data formats, and "flavors" (DataFlavors),
0N/A * which correspond to platform-independent MIME types. This mapping is used
0N/A * by the data transfer subsystem to transfer data between Java and native
0N/A * applications, and between Java applications in separate VMs.
0N/A * <p>
0N/A * In the Sun reference implementation, the default SystemFlavorMap is
0N/A * initialized by the file <code>jre/lib/flavormap.properties</code> and the
0N/A * contents of the URL referenced by the AWT property
0N/A * <code>AWT.DnD.flavorMapFileURL</code>. See <code>flavormap.properties</code>
0N/A * for details.
0N/A *
0N/A * @since 1.2
0N/A */
0N/Apublic final class SystemFlavorMap implements FlavorMap, FlavorTable {
0N/A
0N/A /**
0N/A * Constant prefix used to tag Java types converted to native platform
0N/A * type.
0N/A */
0N/A private static String JavaMIME = "JAVA_DATAFLAVOR:";
0N/A
0N/A /**
0N/A * System singleton which maps a thread's ClassLoader to a SystemFlavorMap.
0N/A */
0N/A private static final WeakHashMap flavorMaps = new WeakHashMap();
0N/A
0N/A /**
0N/A * Copied from java.util.Properties.
0N/A */
0N/A private static final String keyValueSeparators = "=: \t\r\n\f";
0N/A private static final String strictKeyValueSeparators = "=:";
0N/A private static final String whiteSpaceChars = " \t\r\n\f";
0N/A
0N/A /**
0N/A * The list of valid, decoded text flavor representation classes, in order
0N/A * from best to worst.
0N/A */
0N/A private static final String[] UNICODE_TEXT_CLASSES = {
0N/A "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
0N/A };
0N/A
0N/A /**
0N/A * The list of valid, encoded text flavor representation classes, in order
0N/A * from best to worst.
0N/A */
0N/A private static final String[] ENCODED_TEXT_CLASSES = {
0N/A "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
0N/A };
0N/A
0N/A /**
0N/A * A String representing text/plain MIME type.
0N/A */
0N/A private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
0N/A
0N/A /**
0N/A * This constant is passed to flavorToNativeLookup() to indicate that a
0N/A * a native should be synthesized, stored, and returned by encoding the
0N/A * DataFlavor's MIME type in case if the DataFlavor is not found in
0N/A * 'flavorToNative' map.
0N/A */
0N/A private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
0N/A
0N/A /**
0N/A * Maps native Strings to Lists of DataFlavors (or base type Strings for
0N/A * text DataFlavors).
86N/A * Do not use the field directly, use getNativeToFlavor() instead.
0N/A */
0N/A private Map nativeToFlavor = new HashMap();
0N/A
0N/A /**
86N/A * Accessor to nativeToFlavor map. Since we use lazy initialization we must
86N/A * use this accessor instead of direct access to the field which may not be
86N/A * initialized yet. This method will initialize the field if needed.
86N/A *
86N/A * @return nativeToFlavor
86N/A */
86N/A private Map getNativeToFlavor() {
86N/A if (!isMapInitialized) {
86N/A initSystemFlavorMap();
86N/A }
86N/A return nativeToFlavor;
86N/A }
86N/A
86N/A /**
0N/A * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
0N/A * native Strings.
86N/A * Do not use the field directly, use getFlavorToNative() instead.
0N/A */
0N/A private Map flavorToNative = new HashMap();
0N/A
0N/A /**
86N/A * Accessor to flavorToNative map. Since we use lazy initialization we must
86N/A * use this accessor instead of direct access to the field which may not be
86N/A * initialized yet. This method will initialize the field if needed.
86N/A *
86N/A * @return flavorToNative
86N/A */
86N/A private synchronized Map getFlavorToNative() {
86N/A if (!isMapInitialized) {
86N/A initSystemFlavorMap();
86N/A }
86N/A return flavorToNative;
86N/A }
86N/A
86N/A /**
86N/A * Shows if the object has been initialized.
86N/A */
86N/A private boolean isMapInitialized = false;
86N/A
86N/A /**
0N/A * Caches the result of getNativesForFlavor(). Maps DataFlavors to
0N/A * SoftReferences which reference Lists of String natives.
0N/A */
0N/A private Map getNativesForFlavorCache = new HashMap();
0N/A
0N/A /**
0N/A * Caches the result getFlavorsForNative(). Maps String natives to
0N/A * SoftReferences which reference Lists of DataFlavors.
0N/A */
0N/A private Map getFlavorsForNativeCache = new HashMap();
0N/A
0N/A /**
0N/A * Dynamic mapping generation used for text mappings should not be applied
0N/A * to the DataFlavors and String natives for which the mappings have been
0N/A * explicitly specified with setFlavorsForNative() or
0N/A * setNativesForFlavor(). This keeps all such keys.
0N/A */
0N/A private Set disabledMappingGenerationKeys = new HashSet();
0N/A
0N/A /**
0N/A * Returns the default FlavorMap for this thread's ClassLoader.
0N/A */
0N/A public static FlavorMap getDefaultFlavorMap() {
0N/A ClassLoader contextClassLoader =
0N/A Thread.currentThread().getContextClassLoader();
0N/A if (contextClassLoader == null) {
0N/A contextClassLoader = ClassLoader.getSystemClassLoader();
0N/A }
0N/A
0N/A FlavorMap fm;
0N/A
0N/A synchronized(flavorMaps) {
0N/A fm = (FlavorMap)flavorMaps.get(contextClassLoader);
0N/A if (fm == null) {
0N/A fm = new SystemFlavorMap();
0N/A flavorMaps.put(contextClassLoader, fm);
0N/A }
0N/A }
0N/A
0N/A return fm;
0N/A }
0N/A
86N/A private SystemFlavorMap() {
86N/A }
86N/A
0N/A /**
86N/A * Initializes a SystemFlavorMap by reading flavormap.properties and
0N/A * AWT.DnD.flavorMapFileURL.
86N/A * For thread-safety must be called under lock on this.
0N/A */
86N/A private void initSystemFlavorMap() {
86N/A if (isMapInitialized) {
86N/A return;
86N/A }
86N/A
86N/A isMapInitialized = true;
86N/A BufferedReader flavormapDotProperties =
0N/A java.security.AccessController.doPrivileged(
86N/A new java.security.PrivilegedAction<BufferedReader>() {
86N/A public BufferedReader run() {
0N/A String fileName =
0N/A System.getProperty("java.home") +
0N/A File.separator +
0N/A "lib" +
0N/A File.separator +
0N/A "flavormap.properties";
0N/A try {
0N/A return new BufferedReader
0N/A (new InputStreamReader
0N/A (new File(fileName).toURI().toURL().openStream(), "ISO-8859-1"));
0N/A } catch (MalformedURLException e) {
0N/A System.err.println("MalformedURLException:" + e + " while loading default flavormap.properties file:" + fileName);
0N/A } catch (IOException e) {
0N/A System.err.println("IOException:" + e + " while loading default flavormap.properties file:" + fileName);
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A
86N/A BufferedReader flavormapURL =
0N/A java.security.AccessController.doPrivileged(
86N/A new java.security.PrivilegedAction<BufferedReader>() {
86N/A public BufferedReader run() {
86N/A String url = Toolkit.getProperty("AWT.DnD.flavorMapFileURL", null);
0N/A
0N/A if (url == null) {
0N/A return null;
0N/A }
0N/A
0N/A try {
0N/A return new BufferedReader
0N/A (new InputStreamReader
0N/A (new URL(url).openStream(), "ISO-8859-1"));
0N/A } catch (MalformedURLException e) {
0N/A System.err.println("MalformedURLException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
0N/A } catch (IOException e) {
0N/A System.err.println("IOException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A
0N/A if (flavormapDotProperties != null) {
0N/A try {
0N/A parseAndStoreReader(flavormapDotProperties);
0N/A } catch (IOException e) {
0N/A System.err.println("IOException:" + e + " while parsing default flavormap.properties file");
0N/A }
0N/A }
0N/A
0N/A if (flavormapURL != null) {
0N/A try {
0N/A parseAndStoreReader(flavormapURL);
0N/A } catch (IOException e) {
0N/A System.err.println("IOException:" + e + " while parsing AWT.DnD.flavorMapFileURL");
0N/A }
0N/A }
0N/A }
0N/A /**
0N/A * Copied code from java.util.Properties. Parsing the data ourselves is the
0N/A * only way to handle duplicate keys and values.
0N/A */
0N/A private void parseAndStoreReader(BufferedReader in) throws IOException {
0N/A while (true) {
0N/A // Get next line
0N/A String line = in.readLine();
0N/A if (line == null) {
0N/A return;
0N/A }
0N/A
0N/A if (line.length() > 0) {
0N/A // Continue lines that end in slashes if they are not comments
0N/A char firstChar = line.charAt(0);
0N/A if (firstChar != '#' && firstChar != '!') {
0N/A while (continueLine(line)) {
0N/A String nextLine = in.readLine();
0N/A if (nextLine == null) {
215N/A nextLine = "";
0N/A }
0N/A String loppedLine =
0N/A line.substring(0, line.length() - 1);
0N/A // Advance beyond whitespace on new line
0N/A int startIndex = 0;
0N/A for(; startIndex < nextLine.length(); startIndex++) {
0N/A if (whiteSpaceChars.
0N/A indexOf(nextLine.charAt(startIndex)) == -1)
0N/A {
0N/A break;
0N/A }
0N/A }
0N/A nextLine = nextLine.substring(startIndex,
0N/A nextLine.length());
215N/A line = loppedLine+nextLine;
0N/A }
0N/A
0N/A // Find start of key
0N/A int len = line.length();
0N/A int keyStart = 0;
0N/A for(; keyStart < len; keyStart++) {
0N/A if(whiteSpaceChars.
0N/A indexOf(line.charAt(keyStart)) == -1) {
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // Blank lines are ignored
0N/A if (keyStart == len) {
0N/A continue;
0N/A }
0N/A
0N/A // Find separation between key and value
0N/A int separatorIndex = keyStart;
0N/A for(; separatorIndex < len; separatorIndex++) {
0N/A char currentChar = line.charAt(separatorIndex);
0N/A if (currentChar == '\\') {
0N/A separatorIndex++;
0N/A } else if (keyValueSeparators.
0N/A indexOf(currentChar) != -1) {
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // Skip over whitespace after key if any
0N/A int valueIndex = separatorIndex;
0N/A for (; valueIndex < len; valueIndex++) {
0N/A if (whiteSpaceChars.
0N/A indexOf(line.charAt(valueIndex)) == -1) {
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // Skip over one non whitespace key value separators if any
0N/A if (valueIndex < len) {
0N/A if (strictKeyValueSeparators.
0N/A indexOf(line.charAt(valueIndex)) != -1) {
0N/A valueIndex++;
0N/A }
0N/A }
0N/A
0N/A // Skip over white space after other separators if any
0N/A while (valueIndex < len) {
0N/A if (whiteSpaceChars.
0N/A indexOf(line.charAt(valueIndex)) == -1) {
0N/A break;
0N/A }
0N/A valueIndex++;
0N/A }
0N/A
0N/A String key = line.substring(keyStart, separatorIndex);
0N/A String value = (separatorIndex < len)
0N/A ? line.substring(valueIndex, len)
0N/A : "";
0N/A
0N/A // Convert then store key and value
0N/A key = loadConvert(key);
0N/A value = loadConvert(value);
0N/A
0N/A try {
0N/A MimeType mime = new MimeType(value);
0N/A if ("text".equals(mime.getPrimaryType())) {
0N/A String charset = mime.getParameter("charset");
0N/A if (DataTransferer.doesSubtypeSupportCharset
0N/A (mime.getSubType(), charset))
0N/A {
0N/A // We need to store the charset and eoln
0N/A // parameters, if any, so that the
0N/A // DataTransferer will have this information
0N/A // for conversion into the native format.
0N/A DataTransferer transferer =
0N/A DataTransferer.getInstance();
0N/A if (transferer != null) {
0N/A transferer.registerTextFlavorProperties
0N/A (key, charset,
0N/A mime.getParameter("eoln"),
0N/A mime.getParameter("terminators"));
0N/A }
0N/A }
0N/A
0N/A // But don't store any of these parameters in the
0N/A // DataFlavor itself for any text natives (even
0N/A // non-charset ones). The SystemFlavorMap will
0N/A // synthesize the appropriate mappings later.
0N/A mime.removeParameter("charset");
0N/A mime.removeParameter("class");
0N/A mime.removeParameter("eoln");
0N/A mime.removeParameter("terminators");
0N/A value = mime.toString();
0N/A }
0N/A } catch (MimeTypeParseException e) {
0N/A e.printStackTrace();
0N/A continue;
0N/A }
0N/A
0N/A DataFlavor flavor;
0N/A try {
0N/A flavor = new DataFlavor(value);
0N/A } catch (Exception e) {
0N/A try {
0N/A flavor = new DataFlavor(value, (String)null);
0N/A } catch (Exception ee) {
0N/A ee.printStackTrace();
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A // For text/* flavors, store mappings in separate maps to
0N/A // enable dynamic mapping generation at a run-time.
0N/A if ("text".equals(flavor.getPrimaryType())) {
86N/A store(value, key, getFlavorToNative());
86N/A store(key, value, getNativeToFlavor());
0N/A } else {
86N/A store(flavor, key, getFlavorToNative());
86N/A store(key, flavor, getNativeToFlavor());
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Copied from java.util.Properties.
0N/A */
0N/A private boolean continueLine (String line) {
0N/A int slashCount = 0;
0N/A int index = line.length() - 1;
0N/A while((index >= 0) && (line.charAt(index--) == '\\')) {
0N/A slashCount++;
0N/A }
0N/A return (slashCount % 2 == 1);
0N/A }
0N/A
0N/A /**
0N/A * Copied from java.util.Properties.
0N/A */
0N/A private String loadConvert(String theString) {
0N/A char aChar;
0N/A int len = theString.length();
0N/A StringBuilder outBuffer = new StringBuilder(len);
0N/A
0N/A for (int x = 0; x < len; ) {
0N/A aChar = theString.charAt(x++);
0N/A if (aChar == '\\') {
0N/A aChar = theString.charAt(x++);
0N/A if (aChar == 'u') {
0N/A // Read the xxxx
0N/A int value = 0;
0N/A for (int i = 0; i < 4; i++) {
0N/A aChar = theString.charAt(x++);
0N/A switch (aChar) {
0N/A case '0': case '1': case '2': case '3': case '4':
0N/A case '5': case '6': case '7': case '8': case '9': {
0N/A value = (value << 4) + aChar - '0';
0N/A break;
0N/A }
0N/A case 'a': case 'b': case 'c':
0N/A case 'd': case 'e': case 'f': {
0N/A value = (value << 4) + 10 + aChar - 'a';
0N/A break;
0N/A }
0N/A case 'A': case 'B': case 'C':
0N/A case 'D': case 'E': case 'F': {
0N/A value = (value << 4) + 10 + aChar - 'A';
0N/A break;
0N/A }
0N/A default: {
0N/A throw new IllegalArgumentException(
0N/A "Malformed \\uxxxx encoding.");
0N/A }
0N/A }
0N/A }
0N/A outBuffer.append((char)value);
0N/A } else {
0N/A if (aChar == 't') {
0N/A aChar = '\t';
0N/A } else if (aChar == 'r') {
0N/A aChar = '\r';
0N/A } else if (aChar == 'n') {
0N/A aChar = '\n';
0N/A } else if (aChar == 'f') {
0N/A aChar = '\f';
0N/A }
0N/A outBuffer.append(aChar);
0N/A }
0N/A } else {
0N/A outBuffer.append(aChar);
0N/A }
0N/A }
0N/A return outBuffer.toString();
0N/A }
0N/A
0N/A /**
0N/A * Stores the listed object under the specified hash key in map. Unlike a
0N/A * standard map, the listed object will not replace any object already at
0N/A * the appropriate Map location, but rather will be appended to a List
0N/A * stored in that location.
0N/A */
0N/A private void store(Object hashed, Object listed, Map map) {
0N/A List list = (List)map.get(hashed);
0N/A if (list == null) {
0N/A list = new ArrayList(1);
0N/A map.put(hashed, list);
0N/A }
0N/A if (!list.contains(listed)) {
0N/A list.add(listed);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
0N/A * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
0N/A * case, a new DataFlavor is synthesized, stored, and returned, if and
0N/A * only if the specified native is encoded as a Java MIME type.
0N/A */
0N/A private List nativeToFlavorLookup(String nat) {
86N/A List flavors = (List)getNativeToFlavor().get(nat);
0N/A
0N/A if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
0N/A DataTransferer transferer = DataTransferer.getInstance();
0N/A if (transferer != null) {
0N/A List platformFlavors =
0N/A transferer.getPlatformMappingsForNative(nat);
0N/A if (!platformFlavors.isEmpty()) {
0N/A if (flavors != null) {
0N/A platformFlavors.removeAll(new HashSet(flavors));
0N/A // Prepending the platform-specific mappings ensures
0N/A // that the flavors added with
0N/A // addFlavorForUnencodedNative() are at the end of
0N/A // list.
0N/A platformFlavors.addAll(flavors);
0N/A }
0N/A flavors = platformFlavors;
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (flavors == null && isJavaMIMEType(nat)) {
0N/A String decoded = decodeJavaMIMEType(nat);
0N/A DataFlavor flavor = null;
0N/A
0N/A try {
0N/A flavor = new DataFlavor(decoded);
0N/A } catch (Exception e) {
0N/A System.err.println("Exception \"" + e.getClass().getName() +
0N/A ": " + e.getMessage() +
0N/A "\"while constructing DataFlavor for: " +
0N/A decoded);
0N/A }
0N/A
0N/A if (flavor != null) {
0N/A flavors = new ArrayList(1);
86N/A getNativeToFlavor().put(nat, flavors);
0N/A flavors.add(flavor);
0N/A getFlavorsForNativeCache.remove(nat);
0N/A getFlavorsForNativeCache.remove(null);
0N/A
86N/A List natives = (List)getFlavorToNative().get(flavor);
0N/A if (natives == null) {
0N/A natives = new ArrayList(1);
86N/A getFlavorToNative().put(flavor, natives);
0N/A }
0N/A natives.add(nat);
0N/A getNativesForFlavorCache.remove(flavor);
0N/A getNativesForFlavorCache.remove(null);
0N/A }
0N/A }
0N/A
0N/A return (flavors != null) ? flavors : new ArrayList(0);
0N/A }
0N/A
0N/A /**
0N/A * Semantically equivalent to 'flavorToNative.get(flav)'. This method
0N/A * handles the case where 'flav' is not found in 'flavorToNative' depending
0N/A * on the value of passes 'synthesize' parameter. If 'synthesize' is
0N/A * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
0N/A * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
0N/A * and 'flavorToNative' remains unaffected.
0N/A */
0N/A private List flavorToNativeLookup(final DataFlavor flav,
0N/A final boolean synthesize) {
86N/A List natives = (List)getFlavorToNative().get(flav);
0N/A
0N/A if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
0N/A DataTransferer transferer = DataTransferer.getInstance();
0N/A if (transferer != null) {
0N/A List platformNatives =
0N/A transferer.getPlatformMappingsForFlavor(flav);
0N/A if (!platformNatives.isEmpty()) {
0N/A if (natives != null) {
0N/A platformNatives.removeAll(new HashSet(natives));
0N/A // Prepend the platform-specific mappings to ensure
0N/A // that the natives added with
0N/A // addUnencodedNativeForFlavor() are at the end of
0N/A // list.
0N/A platformNatives.addAll(natives);
0N/A }
0N/A natives = platformNatives;
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (natives == null) {
0N/A if (synthesize) {
0N/A String encoded = encodeDataFlavor(flav);
0N/A natives = new ArrayList(1);
86N/A getFlavorToNative().put(flav, natives);
0N/A natives.add(encoded);
0N/A getNativesForFlavorCache.remove(flav);
0N/A getNativesForFlavorCache.remove(null);
0N/A
86N/A List flavors = (List)getNativeToFlavor().get(encoded);
0N/A if (flavors == null) {
0N/A flavors = new ArrayList(1);
86N/A getNativeToFlavor().put(encoded, flavors);
0N/A }
0N/A flavors.add(flav);
0N/A getFlavorsForNativeCache.remove(encoded);
0N/A getFlavorsForNativeCache.remove(null);
0N/A } else {
0N/A natives = new ArrayList(0);
0N/A }
0N/A }
0N/A
0N/A return natives;
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>List</code> of <code>String</code> natives to which the
0N/A * specified <code>DataFlavor</code> can be translated by the data transfer
0N/A * subsystem. The <code>List</code> will be sorted from best native to
0N/A * worst. That is, the first native will best reflect data in the specified
0N/A * flavor to the underlying native platform.
0N/A * <p>
0N/A * If the specified <code>DataFlavor</code> is previously unknown to the
0N/A * data transfer subsystem and the data transfer subsystem is unable to
0N/A * translate this <code>DataFlavor</code> to any existing native, then
0N/A * invoking this method will establish a
0N/A * mapping in both directions between the specified <code>DataFlavor</code>
0N/A * and an encoded version of its MIME type as its native.
0N/A *
0N/A * @param flav the <code>DataFlavor</code> whose corresponding natives
0N/A * should be returned. If <code>null</code> is specified, all
0N/A * natives currently known to the data transfer subsystem are
0N/A * returned in a non-deterministic order.
0N/A * @return a <code>java.util.List</code> of <code>java.lang.String</code>
0N/A * objects which are platform-specific representations of platform-
0N/A * specific data formats
0N/A *
0N/A * @see #encodeDataFlavor
0N/A * @since 1.4
0N/A */
0N/A public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
0N/A List retval = null;
0N/A
0N/A // Check cache, even for null flav
0N/A SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
0N/A if (ref != null) {
0N/A retval = (List)ref.get();
0N/A if (retval != null) {
0N/A // Create a copy, because client code can modify the returned
0N/A // list.
0N/A return new ArrayList(retval);
0N/A }
0N/A }
0N/A
0N/A if (flav == null) {
86N/A retval = new ArrayList(getNativeToFlavor().keySet());
0N/A } else if (disabledMappingGenerationKeys.contains(flav)) {
0N/A // In this case we shouldn't synthesize a native for this flavor,
0N/A // since its mappings were explicitly specified.
0N/A retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
0N/A } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
0N/A
0N/A // For text/* flavors, flavor-to-native mappings specified in
0N/A // flavormap.properties are stored per flavor's base type.
0N/A if ("text".equals(flav.getPrimaryType())) {
86N/A retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
0N/A if (retval != null) {
0N/A // To prevent the List stored in the map from modification.
0N/A retval = new ArrayList(retval);
0N/A }
0N/A }
0N/A
0N/A // Also include text/plain natives, but don't duplicate Strings
86N/A List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
0N/A
0N/A if (textPlainList != null && !textPlainList.isEmpty()) {
0N/A // To prevent the List stored in the map from modification.
0N/A // This also guarantees that removeAll() is supported.
0N/A textPlainList = new ArrayList(textPlainList);
0N/A if (retval != null && !retval.isEmpty()) {
0N/A // Use HashSet to get constant-time performance for search.
0N/A textPlainList.removeAll(new HashSet(retval));
0N/A retval.addAll(textPlainList);
0N/A } else {
0N/A retval = textPlainList;
0N/A }
0N/A }
0N/A
0N/A if (retval == null || retval.isEmpty()) {
0N/A retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
0N/A } else {
0N/A // In this branch it is guaranteed that natives explicitly
0N/A // listed for flav's MIME type were added with
0N/A // addUnencodedNativeForFlavor(), so they have lower priority.
0N/A List explicitList =
0N/A flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
0N/A
0N/A // flavorToNativeLookup() never returns null.
0N/A // It can return an empty List, however.
0N/A if (!explicitList.isEmpty()) {
0N/A // To prevent the List stored in the map from modification.
0N/A // This also guarantees that removeAll() is supported.
0N/A explicitList = new ArrayList(explicitList);
0N/A // Use HashSet to get constant-time performance for search.
0N/A explicitList.removeAll(new HashSet(retval));
0N/A retval.addAll(explicitList);
0N/A }
0N/A }
0N/A } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
86N/A retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
0N/A
0N/A if (retval == null || retval.isEmpty()) {
0N/A retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
0N/A } else {
0N/A // In this branch it is guaranteed that natives explicitly
0N/A // listed for flav's MIME type were added with
0N/A // addUnencodedNativeForFlavor(), so they have lower priority.
0N/A List explicitList =
0N/A flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
0N/A
0N/A // flavorToNativeLookup() never returns null.
0N/A // It can return an empty List, however.
0N/A if (!explicitList.isEmpty()) {
0N/A // To prevent the List stored in the map from modification.
0N/A // This also guarantees that add/removeAll() are supported.
0N/A retval = new ArrayList(retval);
0N/A explicitList = new ArrayList(explicitList);
0N/A // Use HashSet to get constant-time performance for search.
0N/A explicitList.removeAll(new HashSet(retval));
0N/A retval.addAll(explicitList);
0N/A }
0N/A }
0N/A } else {
0N/A retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
0N/A }
0N/A
0N/A getNativesForFlavorCache.put(flav, new SoftReference(retval));
0N/A // Create a copy, because client code can modify the returned list.
0N/A return new ArrayList(retval);
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
0N/A * specified <code>String</code> native can be translated by the data
0N/A * transfer subsystem. The <code>List</code> will be sorted from best
0N/A * <code>DataFlavor</code> to worst. That is, the first
0N/A * <code>DataFlavor</code> will best reflect data in the specified
0N/A * native to a Java application.
0N/A * <p>
0N/A * If the specified native is previously unknown to the data transfer
0N/A * subsystem, and that native has been properly encoded, then invoking this
0N/A * method will establish a mapping in both directions between the specified
0N/A * native and a <code>DataFlavor</code> whose MIME type is a decoded
0N/A * version of the native.
0N/A * <p>
0N/A * If the specified native is not a properly encoded native and the
0N/A * mappings for this native have not been altered with
0N/A * <code>setFlavorsForNative</code>, then the contents of the
0N/A * <code>List</code> is platform dependent, but <code>null</code>
0N/A * cannot be returned.
0N/A *
0N/A * @param nat the native whose corresponding <code>DataFlavor</code>s
0N/A * should be returned. If <code>null</code> is specified, all
0N/A * <code>DataFlavor</code>s currently known to the data transfer
0N/A * subsystem are returned in a non-deterministic order.
0N/A * @return a <code>java.util.List</code> of <code>DataFlavor</code>
0N/A * objects into which platform-specific data in the specified,
0N/A * platform-specific native can be translated
0N/A *
0N/A * @see #encodeJavaMIMEType
0N/A * @since 1.4
0N/A */
0N/A public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
0N/A
0N/A // Check cache, even for null nat
0N/A SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
0N/A if (ref != null) {
0N/A ArrayList retval = (ArrayList)ref.get();
0N/A if (retval != null) {
0N/A return (List)retval.clone();
0N/A }
0N/A }
0N/A
0N/A LinkedList retval = new LinkedList();
0N/A
0N/A if (nat == null) {
0N/A List natives = getNativesForFlavor(null);
0N/A HashSet dups = new HashSet(natives.size());
0N/A
0N/A for (Iterator natives_iter = natives.iterator();
0N/A natives_iter.hasNext(); )
0N/A {
0N/A List flavors =
0N/A getFlavorsForNative((String)natives_iter.next());
0N/A for (Iterator flavors_iter = flavors.iterator();
0N/A flavors_iter.hasNext(); )
0N/A {
0N/A Object flavor = flavors_iter.next();
0N/A if (dups.add(flavor)) {
0N/A retval.add(flavor);
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A List flavors = nativeToFlavorLookup(nat);
0N/A
0N/A if (disabledMappingGenerationKeys.contains(nat)) {
0N/A return flavors;
0N/A }
0N/A
0N/A HashSet dups = new HashSet(flavors.size());
0N/A
0N/A List flavorsAndbaseTypes = nativeToFlavorLookup(nat);
0N/A
0N/A for (Iterator flavorsAndbaseTypes_iter =
0N/A flavorsAndbaseTypes.iterator();
0N/A flavorsAndbaseTypes_iter.hasNext(); )
0N/A {
0N/A Object value = flavorsAndbaseTypes_iter.next();
0N/A if (value instanceof String) {
0N/A String baseType = (String)value;
0N/A String subType = null;
0N/A try {
0N/A MimeType mimeType = new MimeType(baseType);
0N/A subType = mimeType.getSubType();
0N/A } catch (MimeTypeParseException mtpe) {
0N/A // Cannot happen, since we checked all mappings
0N/A // on load from flavormap.properties.
0N/A assert(false);
0N/A }
0N/A if (DataTransferer.doesSubtypeSupportCharset(subType,
0N/A null)) {
0N/A if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
0N/A dups.add(DataFlavor.stringFlavor))
0N/A {
0N/A retval.add(DataFlavor.stringFlavor);
0N/A }
0N/A
0N/A for (int i = 0; i < UNICODE_TEXT_CLASSES.length; i++) {
0N/A DataFlavor toAdd = null;
0N/A try {
0N/A toAdd = new DataFlavor
0N/A (baseType + ";charset=Unicode;class=" +
0N/A UNICODE_TEXT_CLASSES[i]);
0N/A } catch (ClassNotFoundException cannotHappen) {
0N/A }
0N/A if (dups.add(toAdd)) {
0N/A retval.add(toAdd);
0N/A }
0N/A }
0N/A
0N/A for (Iterator charset_iter =
0N/A DataTransferer.standardEncodings();
0N/A charset_iter.hasNext(); )
0N/A {
0N/A String charset = (String)charset_iter.next();
0N/A
0N/A for (int i = 0; i < ENCODED_TEXT_CLASSES.length;
0N/A i++)
0N/A {
0N/A DataFlavor toAdd = null;
0N/A try {
0N/A toAdd = new DataFlavor
0N/A (baseType + ";charset=" + charset +
0N/A ";class=" + ENCODED_TEXT_CLASSES[i]);
0N/A } catch (ClassNotFoundException cannotHappen) {
0N/A }
0N/A
0N/A // Check for equality to plainTextFlavor so
0N/A // that we can ensure that the exact charset of
0N/A // plainTextFlavor, not the canonical charset
0N/A // or another equivalent charset with a
0N/A // different name, is used.
0N/A if (toAdd.equals(DataFlavor.plainTextFlavor)) {
0N/A toAdd = DataFlavor.plainTextFlavor;
0N/A }
0N/A
0N/A if (dups.add(toAdd)) {
0N/A retval.add(toAdd);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
0N/A dups.add(DataFlavor.plainTextFlavor))
0N/A {
0N/A retval.add(DataFlavor.plainTextFlavor);
0N/A }
0N/A } else {
0N/A // Non-charset text natives should be treated as
0N/A // opaque, 8-bit data in any of its various
0N/A // representations.
0N/A for (int i = 0; i < ENCODED_TEXT_CLASSES.length; i++) {
0N/A DataFlavor toAdd = null;
0N/A try {
0N/A toAdd = new DataFlavor(baseType +
0N/A ";class=" + ENCODED_TEXT_CLASSES[i]);
0N/A } catch (ClassNotFoundException cannotHappen) {
0N/A }
0N/A
0N/A if (dups.add(toAdd)) {
0N/A retval.add(toAdd);
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A DataFlavor flavor = (DataFlavor)value;
0N/A if (dups.add(flavor)) {
0N/A retval.add(flavor);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A ArrayList arrayList = new ArrayList(retval);
0N/A getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
0N/A return (List)arrayList.clone();
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
0N/A * their most preferred <code>String</code> native. Each native value will
0N/A * be the same as the first native in the List returned by
0N/A * <code>getNativesForFlavor</code> for the specified flavor.
0N/A * <p>
0N/A * If a specified <code>DataFlavor</code> is previously unknown to the
0N/A * data transfer subsystem, then invoking this method will establish a
0N/A * mapping in both directions between the specified <code>DataFlavor</code>
0N/A * and an encoded version of its MIME type as its native.
0N/A *
0N/A * @param flavors an array of <code>DataFlavor</code>s which will be the
0N/A * key set of the returned <code>Map</code>. If <code>null</code> is
0N/A * specified, a mapping of all <code>DataFlavor</code>s known to the
0N/A * data transfer subsystem to their most preferred
0N/A * <code>String</code> natives will be returned.
0N/A * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
0N/A * <code>String</code> natives
0N/A *
0N/A * @see #getNativesForFlavor
0N/A * @see #encodeDataFlavor
0N/A */
0N/A public synchronized Map<DataFlavor,String>
0N/A getNativesForFlavors(DataFlavor[] flavors)
0N/A {
0N/A // Use getNativesForFlavor to generate extra natives for text flavors
0N/A // and stringFlavor
0N/A
0N/A if (flavors == null) {
0N/A List flavor_list = getFlavorsForNative(null);
0N/A flavors = new DataFlavor[flavor_list.size()];
0N/A flavor_list.toArray(flavors);
0N/A }
0N/A
0N/A HashMap retval = new HashMap(flavors.length, 1.0f);
0N/A for (int i = 0; i < flavors.length; i++) {
0N/A List natives = getNativesForFlavor(flavors[i]);
0N/A String nat = (natives.isEmpty()) ? null : (String)natives.get(0);
0N/A retval.put(flavors[i], nat);
0N/A }
0N/A
0N/A return retval;
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>Map</code> of the specified <code>String</code> natives
0N/A * to their most preferred <code>DataFlavor</code>. Each
0N/A * <code>DataFlavor</code> value will be the same as the first
0N/A * <code>DataFlavor</code> in the List returned by
0N/A * <code>getFlavorsForNative</code> for the specified native.
0N/A * <p>
0N/A * If a specified native is previously unknown to the data transfer
0N/A * subsystem, and that native has been properly encoded, then invoking this
0N/A * method will establish a mapping in both directions between the specified
0N/A * native and a <code>DataFlavor</code> whose MIME type is a decoded
0N/A * version of the native.
0N/A *
0N/A * @param natives an array of <code>String</code>s which will be the
0N/A * key set of the returned <code>Map</code>. If <code>null</code> is
0N/A * specified, a mapping of all supported <code>String</code> natives
0N/A * to their most preferred <code>DataFlavor</code>s will be
0N/A * returned.
0N/A * @return a <code>java.util.Map</code> of <code>String</code> natives to
0N/A * <code>DataFlavor</code>s
0N/A *
0N/A * @see #getFlavorsForNative
0N/A * @see #encodeJavaMIMEType
0N/A */
0N/A public synchronized Map<String,DataFlavor>
0N/A getFlavorsForNatives(String[] natives)
0N/A {
0N/A // Use getFlavorsForNative to generate extra flavors for text natives
0N/A
0N/A if (natives == null) {
0N/A List native_list = getNativesForFlavor(null);
0N/A natives = new String[native_list.size()];
0N/A native_list.toArray(natives);
0N/A }
0N/A
0N/A HashMap retval = new HashMap(natives.length, 1.0f);
0N/A for (int i = 0; i < natives.length; i++) {
0N/A List flavors = getFlavorsForNative(natives[i]);
0N/A DataFlavor flav = (flavors.isEmpty())
0N/A ? null : (DataFlavor)flavors.get(0);
0N/A retval.put(natives[i], flav);
0N/A }
0N/A
0N/A return retval;
0N/A }
0N/A
0N/A /**
0N/A * Adds a mapping from the specified <code>DataFlavor</code> (and all
0N/A * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
0N/A * to the specified <code>String</code> native.
0N/A * Unlike <code>getNativesForFlavor</code>, the mapping will only be
0N/A * established in one direction, and the native will not be encoded. To
0N/A * establish a two-way mapping, call
0N/A * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
0N/A * be of lower priority than any existing mapping.
0N/A * This method has no effect if a mapping from the specified or equal
0N/A * <code>DataFlavor</code> to the specified <code>String</code> native
0N/A * already exists.
0N/A *
0N/A * @param flav the <code>DataFlavor</code> key for the mapping
0N/A * @param nat the <code>String</code> native value for the mapping
0N/A * @throws NullPointerException if flav or nat is <code>null</code>
0N/A *
0N/A * @see #addFlavorForUnencodedNative
0N/A * @since 1.4
0N/A */
0N/A public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
0N/A String nat) {
0N/A if (flav == null || nat == null) {
0N/A throw new NullPointerException("null arguments not permitted");
0N/A }
0N/A
86N/A List natives = (List)getFlavorToNative().get(flav);
0N/A if (natives == null) {
0N/A natives = new ArrayList(1);
86N/A getFlavorToNative().put(flav, natives);
0N/A } else if (natives.contains(nat)) {
0N/A return;
0N/A }
0N/A natives.add(nat);
0N/A getNativesForFlavorCache.remove(flav);
0N/A getNativesForFlavorCache.remove(null);
0N/A }
0N/A
0N/A /**
0N/A * Discards the current mappings for the specified <code>DataFlavor</code>
0N/A * and all <code>DataFlavor</code>s equal to the specified
0N/A * <code>DataFlavor</code>, and creates new mappings to the
0N/A * specified <code>String</code> natives.
0N/A * Unlike <code>getNativesForFlavor</code>, the mappings will only be
0N/A * established in one direction, and the natives will not be encoded. To
0N/A * establish two-way mappings, call <code>setFlavorsForNative</code>
0N/A * as well. The first native in the array will represent the highest
0N/A * priority mapping. Subsequent natives will represent mappings of
0N/A * decreasing priority.
0N/A * <p>
0N/A * If the array contains several elements that reference equal
0N/A * <code>String</code> natives, this method will establish new mappings
0N/A * for the first of those elements and ignore the rest of them.
0N/A * <p>
0N/A * It is recommended that client code not reset mappings established by the
0N/A * data transfer subsystem. This method should only be used for
0N/A * application-level mappings.
0N/A *
0N/A * @param flav the <code>DataFlavor</code> key for the mappings
0N/A * @param natives the <code>String</code> native values for the mappings
0N/A * @throws NullPointerException if flav or natives is <code>null</code>
0N/A * or if natives contains <code>null</code> elements
0N/A *
0N/A * @see #setFlavorsForNative
0N/A * @since 1.4
0N/A */
0N/A public synchronized void setNativesForFlavor(DataFlavor flav,
0N/A String[] natives) {
0N/A if (flav == null || natives == null) {
0N/A throw new NullPointerException("null arguments not permitted");
0N/A }
0N/A
86N/A getFlavorToNative().remove(flav);
0N/A for (int i = 0; i < natives.length; i++) {
0N/A addUnencodedNativeForFlavor(flav, natives[i]);
0N/A }
0N/A disabledMappingGenerationKeys.add(flav);
0N/A // Clear the cache to handle the case of empty natives.
0N/A getNativesForFlavorCache.remove(flav);
0N/A getNativesForFlavorCache.remove(null);
0N/A }
0N/A
0N/A /**
0N/A * Adds a mapping from a single <code>String</code> native to a single
0N/A * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
0N/A * mapping will only be established in one direction, and the native will
0N/A * not be encoded. To establish a two-way mapping, call
0N/A * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
0N/A * be of lower priority than any existing mapping.
0N/A * This method has no effect if a mapping from the specified
0N/A * <code>String</code> native to the specified or equal
0N/A * <code>DataFlavor</code> already exists.
0N/A *
0N/A * @param nat the <code>String</code> native key for the mapping
0N/A * @param flav the <code>DataFlavor</code> value for the mapping
0N/A * @throws NullPointerException if nat or flav is <code>null</code>
0N/A *
0N/A * @see #addUnencodedNativeForFlavor
0N/A * @since 1.4
0N/A */
0N/A public synchronized void addFlavorForUnencodedNative(String nat,
0N/A DataFlavor flav) {
0N/A if (nat == null || flav == null) {
0N/A throw new NullPointerException("null arguments not permitted");
0N/A }
0N/A
86N/A List flavors = (List)getNativeToFlavor().get(nat);
0N/A if (flavors == null) {
0N/A flavors = new ArrayList(1);
86N/A getNativeToFlavor().put(nat, flavors);
0N/A } else if (flavors.contains(flav)) {
0N/A return;
0N/A }
0N/A flavors.add(flav);
0N/A getFlavorsForNativeCache.remove(nat);
0N/A getFlavorsForNativeCache.remove(null);
0N/A }
0N/A
0N/A /**
0N/A * Discards the current mappings for the specified <code>String</code>
0N/A * native, and creates new mappings to the specified
0N/A * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
0N/A * mappings will only be established in one direction, and the natives need
0N/A * not be encoded. To establish two-way mappings, call
0N/A * <code>setNativesForFlavor</code> as well. The first
0N/A * <code>DataFlavor</code> in the array will represent the highest priority
0N/A * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
0N/A * decreasing priority.
0N/A * <p>
0N/A * If the array contains several elements that reference equal
0N/A * <code>DataFlavor</code>s, this method will establish new mappings
0N/A * for the first of those elements and ignore the rest of them.
0N/A * <p>
0N/A * It is recommended that client code not reset mappings established by the
0N/A * data transfer subsystem. This method should only be used for
0N/A * application-level mappings.
0N/A *
0N/A * @param nat the <code>String</code> native key for the mappings
0N/A * @param flavors the <code>DataFlavor</code> values for the mappings
0N/A * @throws NullPointerException if nat or flavors is <code>null</code>
0N/A * or if flavors contains <code>null</code> elements
0N/A *
0N/A * @see #setNativesForFlavor
0N/A * @since 1.4
0N/A */
0N/A public synchronized void setFlavorsForNative(String nat,
0N/A DataFlavor[] flavors) {
0N/A if (nat == null || flavors == null) {
0N/A throw new NullPointerException("null arguments not permitted");
0N/A }
0N/A
86N/A getNativeToFlavor().remove(nat);
0N/A for (int i = 0; i < flavors.length; i++) {
0N/A addFlavorForUnencodedNative(nat, flavors[i]);
0N/A }
0N/A disabledMappingGenerationKeys.add(nat);
0N/A // Clear the cache to handle the case of empty flavors.
0N/A getFlavorsForNativeCache.remove(nat);
0N/A getFlavorsForNativeCache.remove(null);
0N/A }
0N/A
0N/A /**
0N/A * Encodes a MIME type for use as a <code>String</code> native. The format
0N/A * of an encoded representation of a MIME type is implementation-dependent.
0N/A * The only restrictions are:
0N/A * <ul>
0N/A * <li>The encoded representation is <code>null</code> if and only if the
0N/A * MIME type <code>String</code> is <code>null</code>.</li>
0N/A * <li>The encoded representations for two non-<code>null</code> MIME type
0N/A * <code>String</code>s are equal if and only if these <code>String</code>s
0N/A * are equal according to <code>String.equals(Object)</code>.</li>
0N/A * </ul>
0N/A * <p>
0N/A * Sun's reference implementation of this method returns the specified MIME
0N/A * type <code>String</code> prefixed with <code>JAVA_DATAFLAVOR:</code>.
0N/A *
0N/A * @param mimeType the MIME type to encode
0N/A * @return the encoded <code>String</code>, or <code>null</code> if
0N/A * mimeType is <code>null</code>
0N/A */
0N/A public static String encodeJavaMIMEType(String mimeType) {
0N/A return (mimeType != null)
0N/A ? JavaMIME + mimeType
0N/A : null;
0N/A }
0N/A
0N/A /**
0N/A * Encodes a <code>DataFlavor</code> for use as a <code>String</code>
0N/A * native. The format of an encoded <code>DataFlavor</code> is
0N/A * implementation-dependent. The only restrictions are:
0N/A * <ul>
0N/A * <li>The encoded representation is <code>null</code> if and only if the
0N/A * specified <code>DataFlavor</code> is <code>null</code> or its MIME type
0N/A * <code>String</code> is <code>null</code>.</li>
0N/A * <li>The encoded representations for two non-<code>null</code>
0N/A * <code>DataFlavor</code>s with non-<code>null</code> MIME type
0N/A * <code>String</code>s are equal if and only if the MIME type
0N/A * <code>String</code>s of these <code>DataFlavor</code>s are equal
0N/A * according to <code>String.equals(Object)</code>.</li>
0N/A * </ul>
0N/A * <p>
0N/A * Sun's reference implementation of this method returns the MIME type
0N/A * <code>String</code> of the specified <code>DataFlavor</code> prefixed
0N/A * with <code>JAVA_DATAFLAVOR:</code>.
0N/A *
0N/A * @param flav the <code>DataFlavor</code> to encode
0N/A * @return the encoded <code>String</code>, or <code>null</code> if
0N/A * flav is <code>null</code> or has a <code>null</code> MIME type
0N/A */
0N/A public static String encodeDataFlavor(DataFlavor flav) {
0N/A return (flav != null)
0N/A ? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
0N/A : null;
0N/A }
0N/A
0N/A /**
0N/A * Returns whether the specified <code>String</code> is an encoded Java
0N/A * MIME type.
0N/A *
0N/A * @param str the <code>String</code> to test
0N/A * @return <code>true</code> if the <code>String</code> is encoded;
0N/A * <code>false</code> otherwise
0N/A */
0N/A public static boolean isJavaMIMEType(String str) {
0N/A return (str != null && str.startsWith(JavaMIME, 0));
0N/A }
0N/A
0N/A /**
0N/A * Decodes a <code>String</code> native for use as a Java MIME type.
0N/A *
0N/A * @param nat the <code>String</code> to decode
0N/A * @return the decoded Java MIME type, or <code>null</code> if nat is not
0N/A * an encoded <code>String</code> native
0N/A */
0N/A public static String decodeJavaMIMEType(String nat) {
0N/A return (isJavaMIMEType(nat))
0N/A ? nat.substring(JavaMIME.length(), nat.length()).trim()
0N/A : null;
0N/A }
0N/A
0N/A /**
0N/A * Decodes a <code>String</code> native for use as a
0N/A * <code>DataFlavor</code>.
0N/A *
0N/A * @param nat the <code>String</code> to decode
0N/A * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
0N/A * nat is not an encoded <code>String</code> native
0N/A */
0N/A public static DataFlavor decodeDataFlavor(String nat)
0N/A throws ClassNotFoundException
0N/A {
0N/A String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
0N/A return (retval_str != null)
0N/A ? new DataFlavor(retval_str)
0N/A : null;
0N/A }
0N/A}