Save This Page
Home » openjdk-7 » java » awt » datatransfer » [javadoc | source]
    1   /*
    2    * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.awt.datatransfer;
   27   
   28   import java.awt.Toolkit;
   29   
   30   import java.lang.ref.SoftReference;
   31   
   32   import java.io.BufferedReader;
   33   import java.io.File;
   34   import java.io.InputStreamReader;
   35   import java.io.IOException;
   36   
   37   import java.net.URL;
   38   import java.net.MalformedURLException;
   39   
   40   import java.util.ArrayList;
   41   import java.util.HashMap;
   42   import java.util.HashSet;
   43   import java.util.Iterator;
   44   import java.util.LinkedList;
   45   import java.util.List;
   46   import java.util.Map;
   47   import java.util.Set;
   48   import java.util.WeakHashMap;
   49   
   50   import sun.awt.datatransfer.DataTransferer;
   51   
   52   /**
   53    * The SystemFlavorMap is a configurable map between "natives" (Strings), which
   54    * correspond to platform-specific data formats, and "flavors" (DataFlavors),
   55    * which correspond to platform-independent MIME types. This mapping is used
   56    * by the data transfer subsystem to transfer data between Java and native
   57    * applications, and between Java applications in separate VMs.
   58    * <p>
   59    * In the Sun reference implementation, the default SystemFlavorMap is
   60    * initialized by the file <code>jre/lib/flavormap.properties</code> and the
   61    * contents of the URL referenced by the AWT property
   62    * <code>AWT.DnD.flavorMapFileURL</code>. See <code>flavormap.properties</code>
   63    * for details.
   64    *
   65    * @since 1.2
   66    */
   67   public final class SystemFlavorMap implements FlavorMap, FlavorTable {
   68   
   69       /**
   70        * Constant prefix used to tag Java types converted to native platform
   71        * type.
   72        */
   73       private static String JavaMIME = "JAVA_DATAFLAVOR:";
   74   
   75       /**
   76        * System singleton which maps a thread's ClassLoader to a SystemFlavorMap.
   77        */
   78       private static final WeakHashMap flavorMaps = new WeakHashMap();
   79   
   80       /**
   81        * Copied from java.util.Properties.
   82        */
   83       private static final String keyValueSeparators = "=: \t\r\n\f";
   84       private static final String strictKeyValueSeparators = "=:";
   85       private static final String whiteSpaceChars = " \t\r\n\f";
   86   
   87       /**
   88        * The list of valid, decoded text flavor representation classes, in order
   89        * from best to worst.
   90        */
   91       private static final String[] UNICODE_TEXT_CLASSES = {
   92           "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
   93       };
   94   
   95       /**
   96        * The list of valid, encoded text flavor representation classes, in order
   97        * from best to worst.
   98        */
   99       private static final String[] ENCODED_TEXT_CLASSES = {
  100           "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  101       };
  102   
  103       /**
  104        * A String representing text/plain MIME type.
  105        */
  106       private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
  107   
  108       /**
  109        * This constant is passed to flavorToNativeLookup() to indicate that a
  110        * a native should be synthesized, stored, and returned by encoding the
  111        * DataFlavor's MIME type in case if the DataFlavor is not found in
  112        * 'flavorToNative' map.
  113        */
  114       private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
  115   
  116       /**
  117        * Maps native Strings to Lists of DataFlavors (or base type Strings for
  118        * text DataFlavors).
  119        * Do not use the field directly, use getNativeToFlavor() instead.
  120        */
  121       private Map nativeToFlavor = new HashMap();
  122   
  123       /**
  124        * Accessor to nativeToFlavor map.  Since we use lazy initialization we must
  125        * use this accessor instead of direct access to the field which may not be
  126        * initialized yet.  This method will initialize the field if needed.
  127        *
  128        * @return nativeToFlavor
  129        */
  130       private Map getNativeToFlavor() {
  131           if (!isMapInitialized) {
  132               initSystemFlavorMap();
  133           }
  134           return nativeToFlavor;
  135       }
  136   
  137       /**
  138        * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
  139        * native Strings.
  140        * Do not use the field directly, use getFlavorToNative() instead.
  141        */
  142       private Map flavorToNative = new HashMap();
  143   
  144       /**
  145        * Accessor to flavorToNative map.  Since we use lazy initialization we must
  146        * use this accessor instead of direct access to the field which may not be
  147        * initialized yet.  This method will initialize the field if needed.
  148        *
  149        * @return flavorToNative
  150        */
  151       private synchronized Map getFlavorToNative() {
  152           if (!isMapInitialized) {
  153               initSystemFlavorMap();
  154           }
  155           return flavorToNative;
  156       }
  157   
  158       /**
  159        * Shows if the object has been initialized.
  160        */
  161       private boolean isMapInitialized = false;
  162   
  163       /**
  164        * Caches the result of getNativesForFlavor(). Maps DataFlavors to
  165        * SoftReferences which reference Lists of String natives.
  166        */
  167       private Map getNativesForFlavorCache = new HashMap();
  168   
  169       /**
  170        * Caches the result getFlavorsForNative(). Maps String natives to
  171        * SoftReferences which reference Lists of DataFlavors.
  172        */
  173       private Map getFlavorsForNativeCache = new HashMap();
  174   
  175       /**
  176        * Dynamic mapping generation used for text mappings should not be applied
  177        * to the DataFlavors and String natives for which the mappings have been
  178        * explicitly specified with setFlavorsForNative() or
  179        * setNativesForFlavor(). This keeps all such keys.
  180        */
  181       private Set disabledMappingGenerationKeys = new HashSet();
  182   
  183       /**
  184        * Returns the default FlavorMap for this thread's ClassLoader.
  185        */
  186       public static FlavorMap getDefaultFlavorMap() {
  187           ClassLoader contextClassLoader =
  188               Thread.currentThread().getContextClassLoader();
  189           if (contextClassLoader == null) {
  190               contextClassLoader = ClassLoader.getSystemClassLoader();
  191           }
  192   
  193           FlavorMap fm;
  194   
  195           synchronized(flavorMaps) {
  196               fm = (FlavorMap)flavorMaps.get(contextClassLoader);
  197               if (fm == null) {
  198                   fm = new SystemFlavorMap();
  199                   flavorMaps.put(contextClassLoader, fm);
  200               }
  201           }
  202   
  203           return fm;
  204       }
  205   
  206       private SystemFlavorMap() {
  207       }
  208   
  209       /**
  210        * Initializes a SystemFlavorMap by reading flavormap.properties and
  211        * AWT.DnD.flavorMapFileURL.
  212        * For thread-safety must be called under lock on this.
  213        */
  214       private void initSystemFlavorMap() {
  215           if (isMapInitialized) {
  216               return;
  217           }
  218   
  219           isMapInitialized = true;
  220           BufferedReader flavormapDotProperties =
  221               java.security.AccessController.doPrivileged(
  222                   new java.security.PrivilegedAction<BufferedReader>() {
  223                       public BufferedReader run() {
  224                           String fileName =
  225                               System.getProperty("java.home") +
  226                               File.separator +
  227                               "lib" +
  228                               File.separator +
  229                               "flavormap.properties";
  230                           try {
  231                               return new BufferedReader
  232                                   (new InputStreamReader
  233                                       (new File(fileName).toURI().toURL().openStream(), "ISO-8859-1"));
  234                           } catch (MalformedURLException e) {
  235                               System.err.println("MalformedURLException:" + e + " while loading default flavormap.properties file:" + fileName);
  236                           } catch (IOException e) {
  237                               System.err.println("IOException:" + e + " while loading default flavormap.properties file:" + fileName);
  238                           }
  239                           return null;
  240                       }
  241                   });
  242   
  243           BufferedReader flavormapURL =
  244               java.security.AccessController.doPrivileged(
  245                   new java.security.PrivilegedAction<BufferedReader>() {
  246                       public BufferedReader run() {
  247                           String url = Toolkit.getProperty("AWT.DnD.flavorMapFileURL", null);
  248   
  249                           if (url == null) {
  250                               return null;
  251                           }
  252   
  253                           try {
  254                               return new BufferedReader
  255                                   (new InputStreamReader
  256                                       (new URL(url).openStream(), "ISO-8859-1"));
  257                           } catch (MalformedURLException e) {
  258                               System.err.println("MalformedURLException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
  259                           } catch (IOException e) {
  260                               System.err.println("IOException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
  261                           }
  262                           return null;
  263                       }
  264                   });
  265   
  266           if (flavormapDotProperties != null) {
  267               try {
  268                   parseAndStoreReader(flavormapDotProperties);
  269               } catch (IOException e) {
  270                   System.err.println("IOException:" + e + " while parsing default flavormap.properties file");
  271               }
  272           }
  273   
  274           if (flavormapURL != null) {
  275               try {
  276                   parseAndStoreReader(flavormapURL);
  277               } catch (IOException e) {
  278                   System.err.println("IOException:" + e + " while parsing AWT.DnD.flavorMapFileURL");
  279               }
  280           }
  281       }
  282       /**
  283        * Copied code from java.util.Properties. Parsing the data ourselves is the
  284        * only way to handle duplicate keys and values.
  285        */
  286       private void parseAndStoreReader(BufferedReader in) throws IOException {
  287           while (true) {
  288               // Get next line
  289               String line = in.readLine();
  290               if (line == null) {
  291                   return;
  292               }
  293   
  294               if (line.length() > 0) {
  295                   // Continue lines that end in slashes if they are not comments
  296                   char firstChar = line.charAt(0);
  297                   if (firstChar != '#' && firstChar != '!') {
  298                       while (continueLine(line)) {
  299                           String nextLine = in.readLine();
  300                           if (nextLine == null) {
  301                               nextLine = "";
  302                           }
  303                           String loppedLine =
  304                               line.substring(0, line.length() - 1);
  305                           // Advance beyond whitespace on new line
  306                           int startIndex = 0;
  307                           for(; startIndex < nextLine.length(); startIndex++) {
  308                               if (whiteSpaceChars.
  309                                       indexOf(nextLine.charAt(startIndex)) == -1)
  310                               {
  311                                   break;
  312                               }
  313                           }
  314                           nextLine = nextLine.substring(startIndex,
  315                                                         nextLine.length());
  316                           line = loppedLine+nextLine;
  317                       }
  318   
  319                       // Find start of key
  320                       int len = line.length();
  321                       int keyStart = 0;
  322                       for(; keyStart < len; keyStart++) {
  323                           if(whiteSpaceChars.
  324                                  indexOf(line.charAt(keyStart)) == -1) {
  325                               break;
  326                           }
  327                       }
  328   
  329                       // Blank lines are ignored
  330                       if (keyStart == len) {
  331                           continue;
  332                       }
  333   
  334                       // Find separation between key and value
  335                       int separatorIndex = keyStart;
  336                       for(; separatorIndex < len; separatorIndex++) {
  337                           char currentChar = line.charAt(separatorIndex);
  338                           if (currentChar == '\\') {
  339                               separatorIndex++;
  340                           } else if (keyValueSeparators.
  341                                          indexOf(currentChar) != -1) {
  342                               break;
  343                           }
  344                       }
  345   
  346                       // Skip over whitespace after key if any
  347                       int valueIndex = separatorIndex;
  348                       for (; valueIndex < len; valueIndex++) {
  349                           if (whiteSpaceChars.
  350                                   indexOf(line.charAt(valueIndex)) == -1) {
  351                               break;
  352                           }
  353                       }
  354   
  355                       // Skip over one non whitespace key value separators if any
  356                       if (valueIndex < len) {
  357                           if (strictKeyValueSeparators.
  358                                   indexOf(line.charAt(valueIndex)) != -1) {
  359                               valueIndex++;
  360                           }
  361                       }
  362   
  363                       // Skip over white space after other separators if any
  364                       while (valueIndex < len) {
  365                           if (whiteSpaceChars.
  366                                   indexOf(line.charAt(valueIndex)) == -1) {
  367                               break;
  368                           }
  369                           valueIndex++;
  370                       }
  371   
  372                       String key = line.substring(keyStart, separatorIndex);
  373                       String value = (separatorIndex < len)
  374                           ? line.substring(valueIndex, len)
  375                           : "";
  376   
  377                       // Convert then store key and value
  378                       key = loadConvert(key);
  379                       value = loadConvert(value);
  380   
  381                       try {
  382                           MimeType mime = new MimeType(value);
  383                           if ("text".equals(mime.getPrimaryType())) {
  384                               String charset = mime.getParameter("charset");
  385                               if (DataTransferer.doesSubtypeSupportCharset
  386                                       (mime.getSubType(), charset))
  387                               {
  388                                   // We need to store the charset and eoln
  389                                   // parameters, if any, so that the
  390                                   // DataTransferer will have this information
  391                                   // for conversion into the native format.
  392                                   DataTransferer transferer =
  393                                       DataTransferer.getInstance();
  394                                   if (transferer != null) {
  395                                       transferer.registerTextFlavorProperties
  396                                           (key, charset,
  397                                            mime.getParameter("eoln"),
  398                                            mime.getParameter("terminators"));
  399                                   }
  400                               }
  401   
  402                               // But don't store any of these parameters in the
  403                               // DataFlavor itself for any text natives (even
  404                               // non-charset ones). The SystemFlavorMap will
  405                               // synthesize the appropriate mappings later.
  406                               mime.removeParameter("charset");
  407                               mime.removeParameter("class");
  408                               mime.removeParameter("eoln");
  409                               mime.removeParameter("terminators");
  410                               value = mime.toString();
  411                           }
  412                       } catch (MimeTypeParseException e) {
  413                           e.printStackTrace();
  414                           continue;
  415                       }
  416   
  417                       DataFlavor flavor;
  418                       try {
  419                           flavor = new DataFlavor(value);
  420                       } catch (Exception e) {
  421                           try {
  422                               flavor = new DataFlavor(value, (String)null);
  423                           } catch (Exception ee) {
  424                               ee.printStackTrace();
  425                               continue;
  426                           }
  427                       }
  428   
  429                       // For text/* flavors, store mappings in separate maps to
  430                       // enable dynamic mapping generation at a run-time.
  431                       if ("text".equals(flavor.getPrimaryType())) {
  432                           store(value, key, getFlavorToNative());
  433                           store(key, value, getNativeToFlavor());
  434                       } else {
  435                           store(flavor, key, getFlavorToNative());
  436                           store(key, flavor, getNativeToFlavor());
  437                       }
  438                   }
  439               }
  440           }
  441       }
  442   
  443       /**
  444        * Copied from java.util.Properties.
  445        */
  446       private boolean continueLine (String line) {
  447           int slashCount = 0;
  448           int index = line.length() - 1;
  449           while((index >= 0) && (line.charAt(index--) == '\\')) {
  450               slashCount++;
  451           }
  452           return (slashCount % 2 == 1);
  453       }
  454   
  455       /**
  456        * Copied from java.util.Properties.
  457        */
  458       private String loadConvert(String theString) {
  459           char aChar;
  460           int len = theString.length();
  461           StringBuilder outBuffer = new StringBuilder(len);
  462   
  463           for (int x = 0; x < len; ) {
  464               aChar = theString.charAt(x++);
  465               if (aChar == '\\') {
  466                   aChar = theString.charAt(x++);
  467                   if (aChar == 'u') {
  468                       // Read the xxxx
  469                       int value = 0;
  470                       for (int i = 0; i < 4; i++) {
  471                           aChar = theString.charAt(x++);
  472                           switch (aChar) {
  473                             case '0': case '1': case '2': case '3': case '4':
  474                             case '5': case '6': case '7': case '8': case '9': {
  475                                value = (value << 4) + aChar - '0';
  476                                break;
  477                             }
  478                             case 'a': case 'b': case 'c':
  479                             case 'd': case 'e': case 'f': {
  480                                value = (value << 4) + 10 + aChar - 'a';
  481                                break;
  482                             }
  483                             case 'A': case 'B': case 'C':
  484                             case 'D': case 'E': case 'F': {
  485                                value = (value << 4) + 10 + aChar - 'A';
  486                                break;
  487                             }
  488                             default: {
  489                                 throw new IllegalArgumentException(
  490                                              "Malformed \\uxxxx encoding.");
  491                             }
  492                           }
  493                       }
  494                       outBuffer.append((char)value);
  495                   } else {
  496                       if (aChar == 't') {
  497                           aChar = '\t';
  498                       } else if (aChar == 'r') {
  499                           aChar = '\r';
  500                       } else if (aChar == 'n') {
  501                           aChar = '\n';
  502                       } else if (aChar == 'f') {
  503                           aChar = '\f';
  504                       }
  505                       outBuffer.append(aChar);
  506                   }
  507               } else {
  508                   outBuffer.append(aChar);
  509               }
  510           }
  511           return outBuffer.toString();
  512       }
  513   
  514       /**
  515        * Stores the listed object under the specified hash key in map. Unlike a
  516        * standard map, the listed object will not replace any object already at
  517        * the appropriate Map location, but rather will be appended to a List
  518        * stored in that location.
  519        */
  520       private void store(Object hashed, Object listed, Map map) {
  521           List list = (List)map.get(hashed);
  522           if (list == null) {
  523               list = new ArrayList(1);
  524               map.put(hashed, list);
  525           }
  526           if (!list.contains(listed)) {
  527               list.add(listed);
  528           }
  529       }
  530   
  531       /**
  532        * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
  533        * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
  534        * case, a new DataFlavor is synthesized, stored, and returned, if and
  535        * only if the specified native is encoded as a Java MIME type.
  536        */
  537       private List nativeToFlavorLookup(String nat) {
  538           List flavors = (List)getNativeToFlavor().get(nat);
  539   
  540           if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
  541               DataTransferer transferer = DataTransferer.getInstance();
  542               if (transferer != null) {
  543                   List platformFlavors =
  544                       transferer.getPlatformMappingsForNative(nat);
  545                   if (!platformFlavors.isEmpty()) {
  546                       if (flavors != null) {
  547                           platformFlavors.removeAll(new HashSet(flavors));
  548                           // Prepending the platform-specific mappings ensures
  549                           // that the flavors added with
  550                           // addFlavorForUnencodedNative() are at the end of
  551                           // list.
  552                           platformFlavors.addAll(flavors);
  553                       }
  554                       flavors = platformFlavors;
  555                   }
  556               }
  557           }
  558   
  559           if (flavors == null && isJavaMIMEType(nat)) {
  560               String decoded = decodeJavaMIMEType(nat);
  561               DataFlavor flavor = null;
  562   
  563               try {
  564                   flavor = new DataFlavor(decoded);
  565               } catch (Exception e) {
  566                   System.err.println("Exception \"" + e.getClass().getName() +
  567                                      ": " + e.getMessage()  +
  568                                      "\"while constructing DataFlavor for: " +
  569                                      decoded);
  570               }
  571   
  572               if (flavor != null) {
  573                   flavors = new ArrayList(1);
  574                   getNativeToFlavor().put(nat, flavors);
  575                   flavors.add(flavor);
  576                   getFlavorsForNativeCache.remove(nat);
  577                   getFlavorsForNativeCache.remove(null);
  578   
  579                   List natives = (List)getFlavorToNative().get(flavor);
  580                   if (natives == null) {
  581                       natives = new ArrayList(1);
  582                       getFlavorToNative().put(flavor, natives);
  583                   }
  584                   natives.add(nat);
  585                   getNativesForFlavorCache.remove(flavor);
  586                   getNativesForFlavorCache.remove(null);
  587               }
  588           }
  589   
  590           return (flavors != null) ? flavors : new ArrayList(0);
  591       }
  592   
  593       /**
  594        * Semantically equivalent to 'flavorToNative.get(flav)'. This method
  595        * handles the case where 'flav' is not found in 'flavorToNative' depending
  596        * on the value of passes 'synthesize' parameter. If 'synthesize' is
  597        * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
  598        * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
  599        * and 'flavorToNative' remains unaffected.
  600        */
  601       private List flavorToNativeLookup(final DataFlavor flav,
  602                                         final boolean synthesize) {
  603           List natives = (List)getFlavorToNative().get(flav);
  604   
  605           if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
  606               DataTransferer transferer = DataTransferer.getInstance();
  607               if (transferer != null) {
  608                   List platformNatives =
  609                       transferer.getPlatformMappingsForFlavor(flav);
  610                   if (!platformNatives.isEmpty()) {
  611                       if (natives != null) {
  612                           platformNatives.removeAll(new HashSet(natives));
  613                           // Prepend the platform-specific mappings to ensure
  614                           // that the natives added with
  615                           // addUnencodedNativeForFlavor() are at the end of
  616                           // list.
  617                           platformNatives.addAll(natives);
  618                       }
  619                       natives = platformNatives;
  620                   }
  621               }
  622           }
  623   
  624           if (natives == null) {
  625               if (synthesize) {
  626                   String encoded = encodeDataFlavor(flav);
  627                   natives = new ArrayList(1);
  628                   getFlavorToNative().put(flav, natives);
  629                   natives.add(encoded);
  630                   getNativesForFlavorCache.remove(flav);
  631                   getNativesForFlavorCache.remove(null);
  632   
  633                   List flavors = (List)getNativeToFlavor().get(encoded);
  634                   if (flavors == null) {
  635                       flavors = new ArrayList(1);
  636                       getNativeToFlavor().put(encoded, flavors);
  637                   }
  638                   flavors.add(flav);
  639                   getFlavorsForNativeCache.remove(encoded);
  640                   getFlavorsForNativeCache.remove(null);
  641               } else {
  642                   natives = new ArrayList(0);
  643               }
  644           }
  645   
  646           return natives;
  647       }
  648   
  649       /**
  650        * Returns a <code>List</code> of <code>String</code> natives to which the
  651        * specified <code>DataFlavor</code> can be translated by the data transfer
  652        * subsystem. The <code>List</code> will be sorted from best native to
  653        * worst. That is, the first native will best reflect data in the specified
  654        * flavor to the underlying native platform.
  655        * <p>
  656        * If the specified <code>DataFlavor</code> is previously unknown to the
  657        * data transfer subsystem and the data transfer subsystem is unable to
  658        * translate this <code>DataFlavor</code> to any existing native, then
  659        * invoking this method will establish a
  660        * mapping in both directions between the specified <code>DataFlavor</code>
  661        * and an encoded version of its MIME type as its native.
  662        *
  663        * @param flav the <code>DataFlavor</code> whose corresponding natives
  664        *        should be returned. If <code>null</code> is specified, all
  665        *        natives currently known to the data transfer subsystem are
  666        *        returned in a non-deterministic order.
  667        * @return a <code>java.util.List</code> of <code>java.lang.String</code>
  668        *         objects which are platform-specific representations of platform-
  669        *         specific data formats
  670        *
  671        * @see #encodeDataFlavor
  672        * @since 1.4
  673        */
  674       public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
  675           List retval = null;
  676   
  677           // Check cache, even for null flav
  678           SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
  679           if (ref != null) {
  680               retval = (List)ref.get();
  681               if (retval != null) {
  682                   // Create a copy, because client code can modify the returned
  683                   // list.
  684                   return new ArrayList(retval);
  685               }
  686           }
  687   
  688           if (flav == null) {
  689               retval = new ArrayList(getNativeToFlavor().keySet());
  690           } else if (disabledMappingGenerationKeys.contains(flav)) {
  691               // In this case we shouldn't synthesize a native for this flavor,
  692               // since its mappings were explicitly specified.
  693               retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
  694           } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
  695   
  696               // For text/* flavors, flavor-to-native mappings specified in
  697               // flavormap.properties are stored per flavor's base type.
  698               if ("text".equals(flav.getPrimaryType())) {
  699                   retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
  700                   if (retval != null) {
  701                       // To prevent the List stored in the map from modification.
  702                       retval = new ArrayList(retval);
  703                   }
  704               }
  705   
  706               // Also include text/plain natives, but don't duplicate Strings
  707               List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
  708   
  709               if (textPlainList != null && !textPlainList.isEmpty()) {
  710                   // To prevent the List stored in the map from modification.
  711                   // This also guarantees that removeAll() is supported.
  712                   textPlainList = new ArrayList(textPlainList);
  713                   if (retval != null && !retval.isEmpty()) {
  714                       // Use HashSet to get constant-time performance for search.
  715                       textPlainList.removeAll(new HashSet(retval));
  716                       retval.addAll(textPlainList);
  717                   } else {
  718                       retval = textPlainList;
  719                   }
  720               }
  721   
  722               if (retval == null || retval.isEmpty()) {
  723                   retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
  724               } else {
  725                   // In this branch it is guaranteed that natives explicitly
  726                   // listed for flav's MIME type were added with
  727                   // addUnencodedNativeForFlavor(), so they have lower priority.
  728                   List explicitList =
  729                       flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
  730   
  731                   // flavorToNativeLookup() never returns null.
  732                   // It can return an empty List, however.
  733                   if (!explicitList.isEmpty()) {
  734                       // To prevent the List stored in the map from modification.
  735                       // This also guarantees that removeAll() is supported.
  736                       explicitList = new ArrayList(explicitList);
  737                       // Use HashSet to get constant-time performance for search.
  738                       explicitList.removeAll(new HashSet(retval));
  739                       retval.addAll(explicitList);
  740                   }
  741               }
  742           } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
  743               retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
  744   
  745               if (retval == null || retval.isEmpty()) {
  746                   retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
  747               } else {
  748                   // In this branch it is guaranteed that natives explicitly
  749                   // listed for flav's MIME type were added with
  750                   // addUnencodedNativeForFlavor(), so they have lower priority.
  751                   List explicitList =
  752                       flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
  753   
  754                   // flavorToNativeLookup() never returns null.
  755                   // It can return an empty List, however.
  756                   if (!explicitList.isEmpty()) {
  757                       // To prevent the List stored in the map from modification.
  758                       // This also guarantees that add/removeAll() are supported.
  759                       retval = new ArrayList(retval);
  760                       explicitList = new ArrayList(explicitList);
  761                       // Use HashSet to get constant-time performance for search.
  762                       explicitList.removeAll(new HashSet(retval));
  763                       retval.addAll(explicitList);
  764                   }
  765               }
  766           } else {
  767               retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
  768           }
  769   
  770           getNativesForFlavorCache.put(flav, new SoftReference(retval));
  771           // Create a copy, because client code can modify the returned list.
  772           return new ArrayList(retval);
  773       }
  774   
  775       /**
  776        * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
  777        * specified <code>String</code> native can be translated by the data
  778        * transfer subsystem. The <code>List</code> will be sorted from best
  779        * <code>DataFlavor</code> to worst. That is, the first
  780        * <code>DataFlavor</code> will best reflect data in the specified
  781        * native to a Java application.
  782        * <p>
  783        * If the specified native is previously unknown to the data transfer
  784        * subsystem, and that native has been properly encoded, then invoking this
  785        * method will establish a mapping in both directions between the specified
  786        * native and a <code>DataFlavor</code> whose MIME type is a decoded
  787        * version of the native.
  788        * <p>
  789        * If the specified native is not a properly encoded native and the
  790        * mappings for this native have not been altered with
  791        * <code>setFlavorsForNative</code>, then the contents of the
  792        * <code>List</code> is platform dependent, but <code>null</code>
  793        * cannot be returned.
  794        *
  795        * @param nat the native whose corresponding <code>DataFlavor</code>s
  796        *        should be returned. If <code>null</code> is specified, all
  797        *        <code>DataFlavor</code>s currently known to the data transfer
  798        *        subsystem are returned in a non-deterministic order.
  799        * @return a <code>java.util.List</code> of <code>DataFlavor</code>
  800        *         objects into which platform-specific data in the specified,
  801        *         platform-specific native can be translated
  802        *
  803        * @see #encodeJavaMIMEType
  804        * @since 1.4
  805        */
  806       public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
  807   
  808           // Check cache, even for null nat
  809           SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
  810           if (ref != null) {
  811               ArrayList retval = (ArrayList)ref.get();
  812               if (retval != null) {
  813                   return (List)retval.clone();
  814               }
  815           }
  816   
  817           LinkedList retval = new LinkedList();
  818   
  819           if (nat == null) {
  820               List natives = getNativesForFlavor(null);
  821               HashSet dups = new HashSet(natives.size());
  822   
  823               for (Iterator natives_iter = natives.iterator();
  824                    natives_iter.hasNext(); )
  825               {
  826                   List flavors =
  827                       getFlavorsForNative((String)natives_iter.next());
  828                   for (Iterator flavors_iter = flavors.iterator();
  829                        flavors_iter.hasNext(); )
  830                   {
  831                       Object flavor = flavors_iter.next();
  832                       if (dups.add(flavor)) {
  833                           retval.add(flavor);
  834                       }
  835                   }
  836               }
  837           } else {
  838               List flavors = nativeToFlavorLookup(nat);
  839   
  840               if (disabledMappingGenerationKeys.contains(nat)) {
  841                   return flavors;
  842               }
  843   
  844               HashSet dups = new HashSet(flavors.size());
  845   
  846               List flavorsAndbaseTypes = nativeToFlavorLookup(nat);
  847   
  848               for (Iterator flavorsAndbaseTypes_iter =
  849                        flavorsAndbaseTypes.iterator();
  850                    flavorsAndbaseTypes_iter.hasNext(); )
  851               {
  852                   Object value = flavorsAndbaseTypes_iter.next();
  853                   if (value instanceof String) {
  854                       String baseType = (String)value;
  855                       String subType = null;
  856                       try {
  857                           MimeType mimeType = new MimeType(baseType);
  858                           subType = mimeType.getSubType();
  859                       } catch (MimeTypeParseException mtpe) {
  860                           // Cannot happen, since we checked all mappings
  861                           // on load from flavormap.properties.
  862                           assert(false);
  863                       }
  864                       if (DataTransferer.doesSubtypeSupportCharset(subType,
  865                                                                    null)) {
  866                           if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
  867                               dups.add(DataFlavor.stringFlavor))
  868                           {
  869                               retval.add(DataFlavor.stringFlavor);
  870                           }
  871   
  872                           for (int i = 0; i < UNICODE_TEXT_CLASSES.length; i++) {
  873                               DataFlavor toAdd = null;
  874                               try {
  875                                   toAdd = new DataFlavor
  876                                       (baseType + ";charset=Unicode;class=" +
  877                                        UNICODE_TEXT_CLASSES[i]);
  878                               } catch (ClassNotFoundException cannotHappen) {
  879                               }
  880                               if (dups.add(toAdd)) {
  881                                   retval.add(toAdd);
  882                               }
  883                           }
  884   
  885                           for (Iterator charset_iter =
  886                                    DataTransferer.standardEncodings();
  887                                charset_iter.hasNext(); )
  888                           {
  889                               String charset = (String)charset_iter.next();
  890   
  891                               for (int i = 0; i < ENCODED_TEXT_CLASSES.length;
  892                                    i++)
  893                               {
  894                                   DataFlavor toAdd = null;
  895                                   try {
  896                                       toAdd = new DataFlavor
  897                                           (baseType + ";charset=" + charset +
  898                                            ";class=" + ENCODED_TEXT_CLASSES[i]);
  899                                   } catch (ClassNotFoundException cannotHappen) {
  900                                   }
  901   
  902                                   // Check for equality to plainTextFlavor so
  903                                   // that we can ensure that the exact charset of
  904                                   // plainTextFlavor, not the canonical charset
  905                                   // or another equivalent charset with a
  906                                   // different name, is used.
  907                                   if (toAdd.equals(DataFlavor.plainTextFlavor)) {
  908                                       toAdd = DataFlavor.plainTextFlavor;
  909                                   }
  910   
  911                                   if (dups.add(toAdd)) {
  912                                       retval.add(toAdd);
  913                                   }
  914                               }
  915                           }
  916   
  917                           if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
  918                               dups.add(DataFlavor.plainTextFlavor))
  919                           {
  920                               retval.add(DataFlavor.plainTextFlavor);
  921                           }
  922                       } else {
  923                           // Non-charset text natives should be treated as
  924                           // opaque, 8-bit data in any of its various
  925                           // representations.
  926                           for (int i = 0; i < ENCODED_TEXT_CLASSES.length; i++) {
  927                               DataFlavor toAdd = null;
  928                               try {
  929                                   toAdd = new DataFlavor(baseType +
  930                                        ";class=" + ENCODED_TEXT_CLASSES[i]);
  931                               } catch (ClassNotFoundException cannotHappen) {
  932                               }
  933   
  934                               if (dups.add(toAdd)) {
  935                                   retval.add(toAdd);
  936                               }
  937                           }
  938                       }
  939                   } else {
  940                       DataFlavor flavor = (DataFlavor)value;
  941                       if (dups.add(flavor)) {
  942                           retval.add(flavor);
  943                       }
  944                   }
  945               }
  946           }
  947   
  948           ArrayList arrayList = new ArrayList(retval);
  949           getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
  950           return (List)arrayList.clone();
  951       }
  952   
  953       /**
  954        * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
  955        * their most preferred <code>String</code> native. Each native value will
  956        * be the same as the first native in the List returned by
  957        * <code>getNativesForFlavor</code> for the specified flavor.
  958        * <p>
  959        * If a specified <code>DataFlavor</code> is previously unknown to the
  960        * data transfer subsystem, then invoking this method will establish a
  961        * mapping in both directions between the specified <code>DataFlavor</code>
  962        * and an encoded version of its MIME type as its native.
  963        *
  964        * @param flavors an array of <code>DataFlavor</code>s which will be the
  965        *        key set of the returned <code>Map</code>. If <code>null</code> is
  966        *        specified, a mapping of all <code>DataFlavor</code>s known to the
  967        *        data transfer subsystem to their most preferred
  968        *        <code>String</code> natives will be returned.
  969        * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
  970        *         <code>String</code> natives
  971        *
  972        * @see #getNativesForFlavor
  973        * @see #encodeDataFlavor
  974        */
  975       public synchronized Map<DataFlavor,String>
  976           getNativesForFlavors(DataFlavor[] flavors)
  977       {
  978           // Use getNativesForFlavor to generate extra natives for text flavors
  979           // and stringFlavor
  980   
  981           if (flavors == null) {
  982               List flavor_list = getFlavorsForNative(null);
  983               flavors = new DataFlavor[flavor_list.size()];
  984               flavor_list.toArray(flavors);
  985           }
  986   
  987           HashMap retval = new HashMap(flavors.length, 1.0f);
  988           for (int i = 0; i < flavors.length; i++) {
  989               List natives = getNativesForFlavor(flavors[i]);
  990               String nat = (natives.isEmpty()) ? null : (String)natives.get(0);
  991               retval.put(flavors[i], nat);
  992           }
  993   
  994           return retval;
  995       }
  996   
  997       /**
  998        * Returns a <code>Map</code> of the specified <code>String</code> natives
  999        * to their most preferred <code>DataFlavor</code>. Each
 1000        * <code>DataFlavor</code> value will be the same as the first
 1001        * <code>DataFlavor</code> in the List returned by
 1002        * <code>getFlavorsForNative</code> for the specified native.
 1003        * <p>
 1004        * If a specified native is previously unknown to the data transfer
 1005        * subsystem, and that native has been properly encoded, then invoking this
 1006        * method will establish a mapping in both directions between the specified
 1007        * native and a <code>DataFlavor</code> whose MIME type is a decoded
 1008        * version of the native.
 1009        *
 1010        * @param natives an array of <code>String</code>s which will be the
 1011        *        key set of the returned <code>Map</code>. If <code>null</code> is
 1012        *        specified, a mapping of all supported <code>String</code> natives
 1013        *        to their most preferred <code>DataFlavor</code>s will be
 1014        *        returned.
 1015        * @return a <code>java.util.Map</code> of <code>String</code> natives to
 1016        *         <code>DataFlavor</code>s
 1017        *
 1018        * @see #getFlavorsForNative
 1019        * @see #encodeJavaMIMEType
 1020        */
 1021       public synchronized Map<String,DataFlavor>
 1022           getFlavorsForNatives(String[] natives)
 1023       {
 1024           // Use getFlavorsForNative to generate extra flavors for text natives
 1025   
 1026           if (natives == null) {
 1027               List native_list = getNativesForFlavor(null);
 1028               natives = new String[native_list.size()];
 1029               native_list.toArray(natives);
 1030           }
 1031   
 1032           HashMap retval = new HashMap(natives.length, 1.0f);
 1033           for (int i = 0; i < natives.length; i++) {
 1034               List flavors = getFlavorsForNative(natives[i]);
 1035               DataFlavor flav = (flavors.isEmpty())
 1036                   ? null : (DataFlavor)flavors.get(0);
 1037               retval.put(natives[i], flav);
 1038           }
 1039   
 1040           return retval;
 1041       }
 1042   
 1043       /**
 1044        * Adds a mapping from the specified <code>DataFlavor</code> (and all
 1045        * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
 1046        * to the specified <code>String</code> native.
 1047        * Unlike <code>getNativesForFlavor</code>, the mapping will only be
 1048        * established in one direction, and the native will not be encoded. To
 1049        * establish a two-way mapping, call
 1050        * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
 1051        * be of lower priority than any existing mapping.
 1052        * This method has no effect if a mapping from the specified or equal
 1053        * <code>DataFlavor</code> to the specified <code>String</code> native
 1054        * already exists.
 1055        *
 1056        * @param flav the <code>DataFlavor</code> key for the mapping
 1057        * @param nat the <code>String</code> native value for the mapping
 1058        * @throws NullPointerException if flav or nat is <code>null</code>
 1059        *
 1060        * @see #addFlavorForUnencodedNative
 1061        * @since 1.4
 1062        */
 1063       public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
 1064                                                            String nat) {
 1065           if (flav == null || nat == null) {
 1066               throw new NullPointerException("null arguments not permitted");
 1067           }
 1068   
 1069           List natives = (List)getFlavorToNative().get(flav);
 1070           if (natives == null) {
 1071               natives = new ArrayList(1);
 1072               getFlavorToNative().put(flav, natives);
 1073           } else if (natives.contains(nat)) {
 1074               return;
 1075           }
 1076           natives.add(nat);
 1077           getNativesForFlavorCache.remove(flav);
 1078           getNativesForFlavorCache.remove(null);
 1079       }
 1080   
 1081       /**
 1082        * Discards the current mappings for the specified <code>DataFlavor</code>
 1083        * and all <code>DataFlavor</code>s equal to the specified
 1084        * <code>DataFlavor</code>, and creates new mappings to the
 1085        * specified <code>String</code> natives.
 1086        * Unlike <code>getNativesForFlavor</code>, the mappings will only be
 1087        * established in one direction, and the natives will not be encoded. To
 1088        * establish two-way mappings, call <code>setFlavorsForNative</code>
 1089        * as well. The first native in the array will represent the highest
 1090        * priority mapping. Subsequent natives will represent mappings of
 1091        * decreasing priority.
 1092        * <p>
 1093        * If the array contains several elements that reference equal
 1094        * <code>String</code> natives, this method will establish new mappings
 1095        * for the first of those elements and ignore the rest of them.
 1096        * <p>
 1097        * It is recommended that client code not reset mappings established by the
 1098        * data transfer subsystem. This method should only be used for
 1099        * application-level mappings.
 1100        *
 1101        * @param flav the <code>DataFlavor</code> key for the mappings
 1102        * @param natives the <code>String</code> native values for the mappings
 1103        * @throws NullPointerException if flav or natives is <code>null</code>
 1104        *         or if natives contains <code>null</code> elements
 1105        *
 1106        * @see #setFlavorsForNative
 1107        * @since 1.4
 1108        */
 1109       public synchronized void setNativesForFlavor(DataFlavor flav,
 1110                                                    String[] natives) {
 1111           if (flav == null || natives == null) {
 1112               throw new NullPointerException("null arguments not permitted");
 1113           }
 1114   
 1115           getFlavorToNative().remove(flav);
 1116           for (int i = 0; i < natives.length; i++) {
 1117               addUnencodedNativeForFlavor(flav, natives[i]);
 1118           }
 1119           disabledMappingGenerationKeys.add(flav);
 1120           // Clear the cache to handle the case of empty natives.
 1121           getNativesForFlavorCache.remove(flav);
 1122           getNativesForFlavorCache.remove(null);
 1123       }
 1124   
 1125       /**
 1126        * Adds a mapping from a single <code>String</code> native to a single
 1127        * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
 1128        * mapping will only be established in one direction, and the native will
 1129        * not be encoded. To establish a two-way mapping, call
 1130        * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
 1131        * be of lower priority than any existing mapping.
 1132        * This method has no effect if a mapping from the specified
 1133        * <code>String</code> native to the specified or equal
 1134        * <code>DataFlavor</code> already exists.
 1135        *
 1136        * @param nat the <code>String</code> native key for the mapping
 1137        * @param flav the <code>DataFlavor</code> value for the mapping
 1138        * @throws NullPointerException if nat or flav is <code>null</code>
 1139        *
 1140        * @see #addUnencodedNativeForFlavor
 1141        * @since 1.4
 1142        */
 1143       public synchronized void addFlavorForUnencodedNative(String nat,
 1144                                                            DataFlavor flav) {
 1145           if (nat == null || flav == null) {
 1146               throw new NullPointerException("null arguments not permitted");
 1147           }
 1148   
 1149           List flavors = (List)getNativeToFlavor().get(nat);
 1150           if (flavors == null) {
 1151               flavors = new ArrayList(1);
 1152               getNativeToFlavor().put(nat, flavors);
 1153           } else if (flavors.contains(flav)) {
 1154               return;
 1155           }
 1156           flavors.add(flav);
 1157           getFlavorsForNativeCache.remove(nat);
 1158           getFlavorsForNativeCache.remove(null);
 1159       }
 1160   
 1161       /**
 1162        * Discards the current mappings for the specified <code>String</code>
 1163        * native, and creates new mappings to the specified
 1164        * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
 1165        * mappings will only be established in one direction, and the natives need
 1166        * not be encoded. To establish two-way mappings, call
 1167        * <code>setNativesForFlavor</code> as well. The first
 1168        * <code>DataFlavor</code> in the array will represent the highest priority
 1169        * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
 1170        * decreasing priority.
 1171        * <p>
 1172        * If the array contains several elements that reference equal
 1173        * <code>DataFlavor</code>s, this method will establish new mappings
 1174        * for the first of those elements and ignore the rest of them.
 1175        * <p>
 1176        * It is recommended that client code not reset mappings established by the
 1177        * data transfer subsystem. This method should only be used for
 1178        * application-level mappings.
 1179        *
 1180        * @param nat the <code>String</code> native key for the mappings
 1181        * @param flavors the <code>DataFlavor</code> values for the mappings
 1182        * @throws NullPointerException if nat or flavors is <code>null</code>
 1183        *         or if flavors contains <code>null</code> elements
 1184        *
 1185        * @see #setNativesForFlavor
 1186        * @since 1.4
 1187        */
 1188       public synchronized void setFlavorsForNative(String nat,
 1189                                                    DataFlavor[] flavors) {
 1190           if (nat == null || flavors == null) {
 1191               throw new NullPointerException("null arguments not permitted");
 1192           }
 1193   
 1194           getNativeToFlavor().remove(nat);
 1195           for (int i = 0; i < flavors.length; i++) {
 1196               addFlavorForUnencodedNative(nat, flavors[i]);
 1197           }
 1198           disabledMappingGenerationKeys.add(nat);
 1199           // Clear the cache to handle the case of empty flavors.
 1200           getFlavorsForNativeCache.remove(nat);
 1201           getFlavorsForNativeCache.remove(null);
 1202       }
 1203   
 1204       /**
 1205        * Encodes a MIME type for use as a <code>String</code> native. The format
 1206        * of an encoded representation of a MIME type is implementation-dependent.
 1207        * The only restrictions are:
 1208        * <ul>
 1209        * <li>The encoded representation is <code>null</code> if and only if the
 1210        * MIME type <code>String</code> is <code>null</code>.</li>
 1211        * <li>The encoded representations for two non-<code>null</code> MIME type
 1212        * <code>String</code>s are equal if and only if these <code>String</code>s
 1213        * are equal according to <code>String.equals(Object)</code>.</li>
 1214        * </ul>
 1215        * <p>
 1216        * Sun's reference implementation of this method returns the specified MIME
 1217        * type <code>String</code> prefixed with <code>JAVA_DATAFLAVOR:</code>.
 1218        *
 1219        * @param mimeType the MIME type to encode
 1220        * @return the encoded <code>String</code>, or <code>null</code> if
 1221        *         mimeType is <code>null</code>
 1222        */
 1223       public static String encodeJavaMIMEType(String mimeType) {
 1224           return (mimeType != null)
 1225               ? JavaMIME + mimeType
 1226               : null;
 1227       }
 1228   
 1229       /**
 1230        * Encodes a <code>DataFlavor</code> for use as a <code>String</code>
 1231        * native. The format of an encoded <code>DataFlavor</code> is
 1232        * implementation-dependent. The only restrictions are:
 1233        * <ul>
 1234        * <li>The encoded representation is <code>null</code> if and only if the
 1235        * specified <code>DataFlavor</code> is <code>null</code> or its MIME type
 1236        * <code>String</code> is <code>null</code>.</li>
 1237        * <li>The encoded representations for two non-<code>null</code>
 1238        * <code>DataFlavor</code>s with non-<code>null</code> MIME type
 1239        * <code>String</code>s are equal if and only if the MIME type
 1240        * <code>String</code>s of these <code>DataFlavor</code>s are equal
 1241        * according to <code>String.equals(Object)</code>.</li>
 1242        * </ul>
 1243        * <p>
 1244        * Sun's reference implementation of this method returns the MIME type
 1245        * <code>String</code> of the specified <code>DataFlavor</code> prefixed
 1246        * with <code>JAVA_DATAFLAVOR:</code>.
 1247        *
 1248        * @param flav the <code>DataFlavor</code> to encode
 1249        * @return the encoded <code>String</code>, or <code>null</code> if
 1250        *         flav is <code>null</code> or has a <code>null</code> MIME type
 1251        */
 1252       public static String encodeDataFlavor(DataFlavor flav) {
 1253           return (flav != null)
 1254               ? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
 1255               : null;
 1256       }
 1257   
 1258       /**
 1259        * Returns whether the specified <code>String</code> is an encoded Java
 1260        * MIME type.
 1261        *
 1262        * @param str the <code>String</code> to test
 1263        * @return <code>true</code> if the <code>String</code> is encoded;
 1264        *         <code>false</code> otherwise
 1265        */
 1266       public static boolean isJavaMIMEType(String str) {
 1267           return (str != null && str.startsWith(JavaMIME, 0));
 1268       }
 1269   
 1270       /**
 1271        * Decodes a <code>String</code> native for use as a Java MIME type.
 1272        *
 1273        * @param nat the <code>String</code> to decode
 1274        * @return the decoded Java MIME type, or <code>null</code> if nat is not
 1275        *         an encoded <code>String</code> native
 1276        */
 1277       public static String decodeJavaMIMEType(String nat) {
 1278           return (isJavaMIMEType(nat))
 1279               ? nat.substring(JavaMIME.length(), nat.length()).trim()
 1280               : null;
 1281       }
 1282   
 1283       /**
 1284        * Decodes a <code>String</code> native for use as a
 1285        * <code>DataFlavor</code>.
 1286        *
 1287        * @param nat the <code>String</code> to decode
 1288        * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
 1289        *         nat is not an encoded <code>String</code> native
 1290        */
 1291       public static DataFlavor decodeDataFlavor(String nat)
 1292           throws ClassNotFoundException
 1293       {
 1294           String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
 1295           return (retval_str != null)
 1296               ? new DataFlavor(retval_str)
 1297               : null;
 1298       }
 1299   }

Save This Page
Home » openjdk-7 » java » awt » datatransfer » [javadoc | source]