src/share/classes/java/awt/datatransfer/SystemFlavorMap.java

Print this page




  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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.Collections;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.LinkedHashSet;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Objects;

  48 import java.util.Set;
  49 
  50 import sun.awt.AppContext;
  51 import sun.awt.datatransfer.DataTransferer;
  52 
  53 /**
  54  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  55  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  56  * which correspond to platform-independent MIME types. This mapping is used
  57  * by the data transfer subsystem to transfer data between Java and native
  58  * applications, and between Java applications in separate VMs.
  59  *
  60  * @since 1.2
  61  */
  62 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  63 
  64     /**
  65      * Constant prefix used to tag Java types converted to native platform
  66      * type.
  67      */


 193     private Set<Object> disabledMappingGenerationKeys = new HashSet<>();
 194 
 195     /**
 196      * Returns the default FlavorMap for this thread's ClassLoader.
 197      * @return the default FlavorMap for this thread's ClassLoader
 198      */
 199     public static FlavorMap getDefaultFlavorMap() {
 200         AppContext context = AppContext.getAppContext();
 201         FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY);
 202         if (fm == null) {
 203             fm = new SystemFlavorMap();
 204             context.put(FLAVOR_MAP_KEY, fm);
 205         }
 206         return fm;
 207     }
 208 
 209     private SystemFlavorMap() {
 210     }
 211 
 212     /**
 213      * Initializes a SystemFlavorMap by reading flavormap.properties and
 214      * AWT.DnD.flavorMapFileURL.
 215      * For thread-safety must be called under lock on this.
 216      */
 217     private void initSystemFlavorMap() {
 218         if (isMapInitialized) {
 219             return;
 220         }
 221 
 222         isMapInitialized = true;
 223         BufferedReader flavormapDotProperties =
 224             java.security.AccessController.doPrivileged(
 225                 new java.security.PrivilegedAction<BufferedReader>() {
 226                     public BufferedReader run() {
 227                         String fileName =
 228                             System.getProperty("java.home") +
 229                             File.separator +
 230                             "lib" +
 231                             File.separator +
 232                             "flavormap.properties";
 233                         try {
 234                             return new BufferedReader
 235                                 (new InputStreamReader
 236                                     (new File(fileName).toURI().toURL().openStream(), "ISO-8859-1"));
 237                         } catch (MalformedURLException e) {
 238                             System.err.println("MalformedURLException:" + e + " while loading default flavormap.properties file:" + fileName);
 239                         } catch (IOException e) {
 240                             System.err.println("IOException:" + e + " while loading default flavormap.properties file:" + fileName);
 241                         }
 242                         return null;
 243                     }
 244                 });
 245 
 246         String url =
 247             java.security.AccessController.doPrivileged(
 248                 new java.security.PrivilegedAction<String>() {
 249                     public String run() {
 250                         return Toolkit.getProperty("AWT.DnD.flavorMapFileURL", null);
 251                     }
 252                 });
 253 
 254         if (flavormapDotProperties != null) {
 255             try {
 256                 parseAndStoreReader(flavormapDotProperties);
 257             } catch (IOException e) {
 258                 System.err.println("IOException:" + e + " while parsing default flavormap.properties file");
 259             }
 260         }
 261 
 262         BufferedReader flavormapURL = null;
 263         if (url != null) {
 264             try {
 265                 flavormapURL = new BufferedReader(new InputStreamReader(new URL(url).openStream(), "ISO-8859-1"));
 266             } catch (MalformedURLException e) {
 267                 System.err.println("MalformedURLException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
 268             } catch (IOException e) {
 269                 System.err.println("IOException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
 270             } catch (SecurityException e) {
 271                 // ignored
 272             }
 273         }
 274 
 275         if (flavormapURL != null) {
 276             try {
 277                 parseAndStoreReader(flavormapURL);
 278             } catch (IOException e) {
 279                 System.err.println("IOException:" + e + " while parsing AWT.DnD.flavorMapFileURL");
 280             }
 281         }
 282     }
 283     /**
 284      * Copied code from java.util.Properties. Parsing the data ourselves is the
 285      * only way to handle duplicate keys and values.
 286      */
 287     private void parseAndStoreReader(BufferedReader in) throws IOException {
 288         while (true) {
 289             // Get next line
 290             String line = in.readLine();
 291             if (line == null) {
 292                 return;
 293             }
 294 
 295             if (line.length() > 0) {
 296                 // Continue lines that end in slashes if they are not comments
 297                 char firstChar = line.charAt(0);
 298                 if (firstChar != '#' && firstChar != '!') {
 299                     while (continueLine(line)) {
 300                         String nextLine = in.readLine();
 301                         if (nextLine == null) {
 302                             nextLine = "";
 303                         }
 304                         String loppedLine =
 305                             line.substring(0, line.length() - 1);
 306                         // Advance beyond whitespace on new line
 307                         int startIndex = 0;
 308                         for(; startIndex < nextLine.length(); startIndex++) {
 309                             if (whiteSpaceChars.
 310                                     indexOf(nextLine.charAt(startIndex)) == -1)
 311                             {
 312                                 break;
 313                             }
 314                         }
 315                         nextLine = nextLine.substring(startIndex,
 316                                                       nextLine.length());
 317                         line = loppedLine+nextLine;
 318                     }
 319 
 320                     // Find start of key
 321                     int len = line.length();
 322                     int keyStart = 0;
 323                     for(; keyStart < len; keyStart++) {
 324                         if(whiteSpaceChars.
 325                                indexOf(line.charAt(keyStart)) == -1) {
 326                             break;
 327                         }
 328                     }
 329 
 330                     // Blank lines are ignored
 331                     if (keyStart == len) {
 332                         continue;
 333                     }
 334 
 335                     // Find separation between key and value
 336                     int separatorIndex = keyStart;
 337                     for(; separatorIndex < len; separatorIndex++) {
 338                         char currentChar = line.charAt(separatorIndex);
 339                         if (currentChar == '\\') {
 340                             separatorIndex++;
 341                         } else if (keyValueSeparators.
 342                                        indexOf(currentChar) != -1) {
 343                             break;
 344                         }
 345                     }
 346 
 347                     // Skip over whitespace after key if any
 348                     int valueIndex = separatorIndex;
 349                     for (; valueIndex < len; valueIndex++) {
 350                         if (whiteSpaceChars.
 351                                 indexOf(line.charAt(valueIndex)) == -1) {
 352                             break;
 353                         }
 354                     }
 355 
 356                     // Skip over one non whitespace key value separators if any
 357                     if (valueIndex < len) {
 358                         if (strictKeyValueSeparators.
 359                                 indexOf(line.charAt(valueIndex)) != -1) {
 360                             valueIndex++;
 361                         }
 362                     }
 363 
 364                     // Skip over white space after other separators if any
 365                     while (valueIndex < len) {
 366                         if (whiteSpaceChars.
 367                                 indexOf(line.charAt(valueIndex)) == -1) {
 368                             break;
 369                         }
 370                         valueIndex++;
 371                     }
 372 
 373                     String key = line.substring(keyStart, separatorIndex);
 374                     String value = (separatorIndex < len)
 375                         ? line.substring(valueIndex, len)
 376                         : "";
 377 
 378                     // Convert then store key and value
 379                     key = loadConvert(key);
 380                     value = loadConvert(value);
 381 
 382                     try {
 383                         MimeType mime = new MimeType(value);
 384                         if ("text".equals(mime.getPrimaryType())) {
 385                             String charset = mime.getParameter("charset");
 386                             if (DataTransferer.doesSubtypeSupportCharset
 387                                     (mime.getSubType(), charset))
 388                             {
 389                                 // We need to store the charset and eoln
 390                                 // parameters, if any, so that the
 391                                 // DataTransferer will have this information
 392                                 // for conversion into the native format.
 393                                 DataTransferer transferer =
 394                                     DataTransferer.getInstance();
 395                                 if (transferer != null) {
 396                                     transferer.registerTextFlavorProperties
 397                                         (key, charset,
 398                                          mime.getParameter("eoln"),
 399                                          mime.getParameter("terminators"));
 400                                 }
 401                             }
 402 
 403                             // But don't store any of these parameters in the
 404                             // DataFlavor itself for any text natives (even
 405                             // non-charset ones). The SystemFlavorMap will
 406                             // synthesize the appropriate mappings later.
 407                             mime.removeParameter("charset");
 408                             mime.removeParameter("class");
 409                             mime.removeParameter("eoln");
 410                             mime.removeParameter("terminators");
 411                             value = mime.toString();
 412                         }
 413                     } catch (MimeTypeParseException e) {
 414                         e.printStackTrace();
 415                         continue;
 416                     }
 417 


 425                             ee.printStackTrace();
 426                             continue;
 427                         }
 428                     }
 429 
 430                     final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
 431                     dfs.add(flavor);
 432 
 433                     if ("text".equals(flavor.getPrimaryType())) {
 434                         dfs.addAll(convertMimeTypeToDataFlavors(value));
 435                         store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
 436                     }
 437 
 438                     for (DataFlavor df : dfs) {
 439                         store(df, key, getFlavorToNative());
 440                         store(key, df, getNativeToFlavor());
 441                     }
 442                 }
 443             }
 444         }
 445     }
 446 
 447     /**
 448      * Copied from java.util.Properties.
 449      */
 450     private boolean continueLine (String line) {
 451         int slashCount = 0;
 452         int index = line.length() - 1;
 453         while((index >= 0) && (line.charAt(index--) == '\\')) {
 454             slashCount++;
 455         }
 456         return (slashCount % 2 == 1);
 457     }
 458 
 459     /**
 460      * Copied from java.util.Properties.
 461      */
 462     private String loadConvert(String theString) {
 463         char aChar;
 464         int len = theString.length();
 465         StringBuilder outBuffer = new StringBuilder(len);
 466 
 467         for (int x = 0; x < len; ) {
 468             aChar = theString.charAt(x++);
 469             if (aChar == '\\') {
 470                 aChar = theString.charAt(x++);
 471                 if (aChar == 'u') {
 472                     // Read the xxxx
 473                     int value = 0;
 474                     for (int i = 0; i < 4; i++) {
 475                         aChar = theString.charAt(x++);
 476                         switch (aChar) {
 477                           case '0': case '1': case '2': case '3': case '4':
 478                           case '5': case '6': case '7': case '8': case '9': {
 479                              value = (value << 4) + aChar - '0';
 480                              break;
 481                           }
 482                           case 'a': case 'b': case 'c':
 483                           case 'd': case 'e': case 'f': {
 484                              value = (value << 4) + 10 + aChar - 'a';
 485                              break;
 486                           }
 487                           case 'A': case 'B': case 'C':
 488                           case 'D': case 'E': case 'F': {
 489                              value = (value << 4) + 10 + aChar - 'A';
 490                              break;
 491                           }
 492                           default: {
 493                               throw new IllegalArgumentException(
 494                                            "Malformed \\uxxxx encoding.");
 495                           }
 496                         }
 497                     }
 498                     outBuffer.append((char)value);
 499                 } else {
 500                     if (aChar == 't') {
 501                         aChar = '\t';
 502                     } else if (aChar == 'r') {
 503                         aChar = '\r';
 504                     } else if (aChar == 'n') {
 505                         aChar = '\n';
 506                     } else if (aChar == 'f') {
 507                         aChar = '\f';
 508                     }
 509                     outBuffer.append(aChar);
 510                 }
 511             } else {
 512                 outBuffer.append(aChar);
 513             }
 514         }
 515         return outBuffer.toString();
 516     }
 517 
 518     /**
 519      * Stores the listed object under the specified hash key in map. Unlike a
 520      * standard map, the listed object will not replace any object already at
 521      * the appropriate Map location, but rather will be appended to a List
 522      * stored in that location.
 523      */
 524     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 525         LinkedHashSet<L> list = map.get(hashed);
 526         if (list == null) {
 527             list = new LinkedHashSet<>(1);
 528             map.put(hashed, list);
 529         }
 530         if (!list.contains(listed)) {
 531             list.add(listed);
 532         }
 533     }
 534 
 535     /**
 536      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method




  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.datatransfer;
  27 
  28 import java.awt.Toolkit;
  29 
  30 import java.io.BufferedInputStream;
  31 import java.io.InputStream;
  32 import java.lang.ref.SoftReference;
  33 
  34 import java.io.BufferedReader;
  35 import java.io.File;
  36 import java.io.InputStreamReader;
  37 import java.io.IOException;
  38 
  39 import java.net.URL;
  40 import java.net.MalformedURLException;
  41 
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Collections;
  45 import java.util.HashMap;
  46 import java.util.HashSet;
  47 import java.util.LinkedHashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.Objects;
  51 import java.util.Properties;
  52 import java.util.Set;
  53 
  54 import sun.awt.AppContext;
  55 import sun.awt.datatransfer.DataTransferer;
  56 
  57 /**
  58  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  59  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  60  * which correspond to platform-independent MIME types. This mapping is used
  61  * by the data transfer subsystem to transfer data between Java and native
  62  * applications, and between Java applications in separate VMs.
  63  *
  64  * @since 1.2
  65  */
  66 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  67 
  68     /**
  69      * Constant prefix used to tag Java types converted to native platform
  70      * type.
  71      */


 197     private Set<Object> disabledMappingGenerationKeys = new HashSet<>();
 198 
 199     /**
 200      * Returns the default FlavorMap for this thread's ClassLoader.
 201      * @return the default FlavorMap for this thread's ClassLoader
 202      */
 203     public static FlavorMap getDefaultFlavorMap() {
 204         AppContext context = AppContext.getAppContext();
 205         FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY);
 206         if (fm == null) {
 207             fm = new SystemFlavorMap();
 208             context.put(FLAVOR_MAP_KEY, fm);
 209         }
 210         return fm;
 211     }
 212 
 213     private SystemFlavorMap() {
 214     }
 215 
 216     /**
 217      * Initializes a SystemFlavorMap by reading flavormap.properties

 218      * For thread-safety must be called under lock on this.
 219      */
 220     private void initSystemFlavorMap() {
 221         if (isMapInitialized) {
 222             return;
 223         }

 224         isMapInitialized = true;






































 225 
 226         InputStream is = SystemFlavorMap.class.getResourceAsStream("/sun/awt/datatransfer/flavormap.properties");
 227         if (is == null) {
 228             throw new InternalError("Default flavor mapping not found");








 229         }
 230 
 231         Properties flavorProperties = new Properties();
 232         try (BufferedInputStream bis = new BufferedInputStream(is)) {
 233             flavorProperties.load(bis);
 234         } catch (IOException e) {
 235             throw new InternalError("Error reading default flavor mapping", e);



























































































 236         }
 237 
 238         for (String key : flavorProperties.stringPropertyNames()) {
 239             String[] values = flavorProperties.getProperty(key).split(",");
 240             for (String value : values) {






 241                 try {
 242                     MimeType mime = new MimeType(value);
 243                     if ("text".equals(mime.getPrimaryType())) {
 244                         String charset = mime.getParameter("charset");
 245                         if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))

 246                         {
 247                             // We need to store the charset and eoln
 248                             // parameters, if any, so that the
 249                             // DataTransferer will have this information
 250                             // for conversion into the native format.
 251                             DataTransferer transferer = DataTransferer.getInstance();

 252                             if (transferer != null) {
 253                                 transferer.registerTextFlavorProperties(key, charset,

 254                                                 mime.getParameter("eoln"),
 255                                                 mime.getParameter("terminators"));
 256                             }
 257                         }
 258 
 259                         // But don't store any of these parameters in the
 260                         // DataFlavor itself for any text natives (even
 261                         // non-charset ones). The SystemFlavorMap will
 262                         // synthesize the appropriate mappings later.
 263                         mime.removeParameter("charset");
 264                         mime.removeParameter("class");
 265                         mime.removeParameter("eoln");
 266                         mime.removeParameter("terminators");
 267                         value = mime.toString();
 268                     }
 269                 } catch (MimeTypeParseException e) {
 270                     e.printStackTrace();
 271                     continue;
 272                 }
 273 


 281                         ee.printStackTrace();
 282                         continue;
 283                     }
 284                 }
 285 
 286                 final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
 287                 dfs.add(flavor);
 288 
 289                 if ("text".equals(flavor.getPrimaryType())) {
 290                     dfs.addAll(convertMimeTypeToDataFlavors(value));
 291                     store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
 292                 }
 293 
 294                 for (DataFlavor df : dfs) {
 295                     store(df, key, getFlavorToNative());
 296                     store(key, df, getNativeToFlavor());
 297                 }
 298             }
 299         }
 300     }








































































 301 
 302     /**
 303      * Stores the listed object under the specified hash key in map. Unlike a
 304      * standard map, the listed object will not replace any object already at
 305      * the appropriate Map location, but rather will be appended to a List
 306      * stored in that location.
 307      */
 308     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 309         LinkedHashSet<L> list = map.get(hashed);
 310         if (list == null) {
 311             list = new LinkedHashSet<>(1);
 312             map.put(hashed, list);
 313         }
 314         if (!list.contains(listed)) {
 315             list.add(listed);
 316         }
 317     }
 318 
 319     /**
 320      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method