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 */
72 private static String JavaMIME = "JAVA_DATAFLAVOR:";
73
74 private static final Object FLAVOR_MAP_KEY = new Object();
75
76 /**
77 * Copied from java.util.Properties.
78 */
79 private static final String keyValueSeparators = "=: \t\r\n\f";
80 private static final String strictKeyValueSeparators = "=:";
81 private static final String whiteSpaceChars = " \t\r\n\f";
82
83 /**
84 * The list of valid, decoded text flavor representation classes, in order
85 * from best to worst.
86 */
87 private static final String[] UNICODE_TEXT_CLASSES = {
88 "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
89 };
90
91 /**
92 * The list of valid, encoded text flavor representation classes, in order
93 * from best to worst.
94 */
95 private static final String[] ENCODED_TEXT_CLASSES = {
96 "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
97 };
98
99 /**
100 * A String representing text/plain MIME type.
101 */
102 private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
103
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 try (InputStreamReader isr = new InputStreamReader(is);
232 BufferedReader reader = new BufferedReader(isr)) {
233 String line;
234 while ((line = reader.readLine()) != null) {
235 line = line.trim();
236 if (line.startsWith("#") || line.isEmpty()) continue;
237 while (line.endsWith("\\")) {
238 line = line.substring(0, line.length() - 1) + reader.readLine().trim();
239 }
240 int delimiterPosition = line.indexOf('=');
241 String key = line.substring(0, delimiterPosition).replace("\\ ", " ");
242 String[] values = line.substring(delimiterPosition + 1, line.length()).split(",");
243 for (String value : values) {
244 try {
245 MimeType mime = new MimeType(value);
246 if ("text".equals(mime.getPrimaryType())) {
247 String charset = mime.getParameter("charset");
248 if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))
249 {
250 // We need to store the charset and eoln
251 // parameters, if any, so that the
252 // DataTransferer will have this information
253 // for conversion into the native format.
254 DataTransferer transferer = DataTransferer.getInstance();
255 if (transferer != null) {
256 transferer.registerTextFlavorProperties(key, charset,
257 mime.getParameter("eoln"),
258 mime.getParameter("terminators"));
259 }
260 }
261
262 // But don't store any of these parameters in the
263 // DataFlavor itself for any text natives (even
264 // non-charset ones). The SystemFlavorMap will
288
289 final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
290 dfs.add(flavor);
291
292 if ("text".equals(flavor.getPrimaryType())) {
293 dfs.addAll(convertMimeTypeToDataFlavors(value));
294 store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
295 }
296
297 for (DataFlavor df : dfs) {
298 store(df, key, getFlavorToNative());
299 store(key, df, getNativeToFlavor());
300 }
301 }
302 }
303 } catch (IOException e) {
304 throw new InternalError("Error reading default flavor mapping", e);
305 }
306 }
307
308 /**
309 * Stores the listed object under the specified hash key in map. Unlike a
310 * standard map, the listed object will not replace any object already at
311 * the appropriate Map location, but rather will be appended to a List
312 * stored in that location.
313 */
314 private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
315 LinkedHashSet<L> list = map.get(hashed);
316 if (list == null) {
317 list = new LinkedHashSet<>(1);
318 map.put(hashed, list);
319 }
320 if (!list.contains(listed)) {
321 list.add(listed);
322 }
323 }
324
325 /**
326 * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
327 * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
|
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 */
72 private static String JavaMIME = "JAVA_DATAFLAVOR:";
73
74 private static final Object FLAVOR_MAP_KEY = new Object();
75
76 /**
77 * The list of valid, decoded text flavor representation classes, in order
78 * from best to worst.
79 */
80 private static final String[] UNICODE_TEXT_CLASSES = {
81 "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
82 };
83
84 /**
85 * The list of valid, encoded text flavor representation classes, in order
86 * from best to worst.
87 */
88 private static final String[] ENCODED_TEXT_CLASSES = {
89 "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
90 };
91
92 /**
93 * A String representing text/plain MIME type.
94 */
95 private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
96
214 if (isMapInitialized) {
215 return;
216 }
217 isMapInitialized = true;
218
219 InputStream is = SystemFlavorMap.class.getResourceAsStream("/sun/awt/datatransfer/flavormap.properties");
220 if (is == null) {
221 throw new InternalError("Default flavor mapping not found");
222 }
223
224 try (InputStreamReader isr = new InputStreamReader(is);
225 BufferedReader reader = new BufferedReader(isr)) {
226 String line;
227 while ((line = reader.readLine()) != null) {
228 line = line.trim();
229 if (line.startsWith("#") || line.isEmpty()) continue;
230 while (line.endsWith("\\")) {
231 line = line.substring(0, line.length() - 1) + reader.readLine().trim();
232 }
233 int delimiterPosition = line.indexOf('=');
234 String key = line.substring(0, delimiterPosition).replaceAll("\\ ", " ");
235 String[] values = line.substring(delimiterPosition + 1, line.length()).split(",");
236 for (String value : values) {
237 try {
238 value = loadConvert(value);
239 MimeType mime = new MimeType(value);
240 if ("text".equals(mime.getPrimaryType())) {
241 String charset = mime.getParameter("charset");
242 if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))
243 {
244 // We need to store the charset and eoln
245 // parameters, if any, so that the
246 // DataTransferer will have this information
247 // for conversion into the native format.
248 DataTransferer transferer = DataTransferer.getInstance();
249 if (transferer != null) {
250 transferer.registerTextFlavorProperties(key, charset,
251 mime.getParameter("eoln"),
252 mime.getParameter("terminators"));
253 }
254 }
255
256 // But don't store any of these parameters in the
257 // DataFlavor itself for any text natives (even
258 // non-charset ones). The SystemFlavorMap will
282
283 final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
284 dfs.add(flavor);
285
286 if ("text".equals(flavor.getPrimaryType())) {
287 dfs.addAll(convertMimeTypeToDataFlavors(value));
288 store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
289 }
290
291 for (DataFlavor df : dfs) {
292 store(df, key, getFlavorToNative());
293 store(key, df, getNativeToFlavor());
294 }
295 }
296 }
297 } catch (IOException e) {
298 throw new InternalError("Error reading default flavor mapping", e);
299 }
300 }
301
302 private static String loadConvert(String theString) {
303 char aChar;
304 int len = theString.length();
305 StringBuilder outBuffer = new StringBuilder(len);
306
307 for (int x = 0; x < len; ) {
308 aChar = theString.charAt(x++);
309 if (aChar == '\\') {
310 aChar = theString.charAt(x++);
311 if (aChar == 'u') {
312 // Read the xxxx
313 int value = 0;
314 for (int i = 0; i < 4; i++) {
315 aChar = theString.charAt(x++);
316 switch (aChar) {
317 case '0': case '1': case '2': case '3': case '4':
318 case '5': case '6': case '7': case '8': case '9': {
319 value = (value << 4) + aChar - '0';
320 break;
321 }
322 case 'a': case 'b': case 'c':
323 case 'd': case 'e': case 'f': {
324 value = (value << 4) + 10 + aChar - 'a';
325 break;
326 }
327 case 'A': case 'B': case 'C':
328 case 'D': case 'E': case 'F': {
329 value = (value << 4) + 10 + aChar - 'A';
330 break;
331 }
332 default: {
333 throw new IllegalArgumentException(
334 "Malformed \\uxxxx encoding.");
335 }
336 }
337 }
338 outBuffer.append((char)value);
339 } else {
340 if (aChar == 't') {
341 aChar = '\t';
342 } else if (aChar == 'r') {
343 aChar = '\r';
344 } else if (aChar == 'n') {
345 aChar = '\n';
346 } else if (aChar == 'f') {
347 aChar = '\f';
348 }
349 outBuffer.append(aChar);
350 }
351 } else {
352 outBuffer.append(aChar);
353 }
354 }
355 return outBuffer.toString();
356 }
357
358 /**
359 * Stores the listed object under the specified hash key in map. Unlike a
360 * standard map, the listed object will not replace any object already at
361 * the appropriate Map location, but rather will be appended to a List
362 * stored in that location.
363 */
364 private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
365 LinkedHashSet<L> list = map.get(hashed);
366 if (list == null) {
367 list = new LinkedHashSet<>(1);
368 map.put(hashed, list);
369 }
370 if (!list.contains(listed)) {
371 list.add(listed);
372 }
373 }
374
375 /**
376 * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
377 * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
|