Path: blob/master/src/java.datatransfer/share/classes/sun/datatransfer/DataFlavorUtil.java
41155 views
/*1* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.datatransfer;2627import java.awt.datatransfer.DataFlavor;28import java.awt.datatransfer.FlavorMap;29import java.io.IOException;30import java.io.InputStream;31import java.io.Reader;32import java.lang.reflect.Constructor;33import java.lang.reflect.InvocationTargetException;34import java.lang.reflect.Method;35import java.nio.ByteBuffer;36import java.nio.CharBuffer;37import java.nio.charset.Charset;38import java.nio.charset.IllegalCharsetNameException;39import java.nio.charset.StandardCharsets;40import java.nio.charset.UnsupportedCharsetException;41import java.util.Collections;42import java.util.Comparator;43import java.util.HashMap;44import java.util.Iterator;45import java.util.LinkedHashSet;46import java.util.Map;47import java.util.ServiceLoader;48import java.util.Set;49import java.util.SortedSet;50import java.util.TreeSet;51import java.util.function.Supplier;5253/**54* Utility class with different datatransfer helper functions.55*56* @since 957*/58public class DataFlavorUtil {5960private DataFlavorUtil() {61// Avoid instantiation62}6364private static Comparator<String> getCharsetComparator() {65return CharsetComparator.INSTANCE;66}6768public static Comparator<DataFlavor> getDataFlavorComparator() {69return DataFlavorComparator.INSTANCE;70}7172public static Comparator<Long> getIndexOrderComparator(Map<Long, Integer> indexMap) {73return new IndexOrderComparator(indexMap);74}7576public static Comparator<DataFlavor> getTextFlavorComparator() {77return TextFlavorComparator.INSTANCE;78}7980/**81* Tracks whether a particular text/* MIME type supports the charset82* parameter. The Map is initialized with all of the standard MIME types83* listed in the DataFlavor.selectBestTextFlavor method comment. Additional84* entries may be added during the life of the JRE for text/<other>85* types.86*/87private static final Map<String, Boolean> textMIMESubtypeCharsetSupport;8889static {90Map<String, Boolean> tempMap = new HashMap<>(17);91tempMap.put("sgml", Boolean.TRUE);92tempMap.put("xml", Boolean.TRUE);93tempMap.put("html", Boolean.TRUE);94tempMap.put("enriched", Boolean.TRUE);95tempMap.put("richtext", Boolean.TRUE);96tempMap.put("uri-list", Boolean.TRUE);97tempMap.put("directory", Boolean.TRUE);98tempMap.put("css", Boolean.TRUE);99tempMap.put("calendar", Boolean.TRUE);100tempMap.put("plain", Boolean.TRUE);101tempMap.put("rtf", Boolean.FALSE);102tempMap.put("tab-separated-values", Boolean.FALSE);103tempMap.put("t140", Boolean.FALSE);104tempMap.put("rfc822-headers", Boolean.FALSE);105tempMap.put("parityfec", Boolean.FALSE);106textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap);107}108109/**110* Lazy initialization of Standard Encodings.111*/112private static class StandardEncodingsHolder {113private static final SortedSet<String> standardEncodings = load();114115private static SortedSet<String> load() {116final SortedSet<String> tempSet = new TreeSet<>(getCharsetComparator().reversed());117tempSet.add("US-ASCII");118tempSet.add("ISO-8859-1");119tempSet.add("UTF-8");120tempSet.add("UTF-16BE");121tempSet.add("UTF-16LE");122tempSet.add("UTF-16");123tempSet.add(Charset.defaultCharset().name());124return Collections.unmodifiableSortedSet(tempSet);125}126}127128/**129* Returns a {@code SortedSet} of Strings which are a total order of the130* standard character sets supported by the JRE. The ordering follows the131* same principles as {@link DataFlavor#selectBestTextFlavor(DataFlavor[])}.132* So as to avoid loading all available character converters, optional,133* non-standard, character sets are not included.134*/135public static Set<String> standardEncodings() {136return StandardEncodingsHolder.standardEncodings;137}138139/**140* Converts an arbitrary text encoding to its canonical name.141*/142public static String canonicalName(String encoding) {143if (encoding == null) {144return null;145}146try {147return Charset.forName(encoding).name();148} catch (IllegalCharsetNameException icne) {149return encoding;150} catch (UnsupportedCharsetException uce) {151return encoding;152}153}154155/**156* Tests only whether the flavor's MIME type supports the charset parameter.157* Must only be called for flavors with a primary type of "text".158*/159public static boolean doesSubtypeSupportCharset(DataFlavor flavor) {160String subType = flavor.getSubType();161if (subType == null) {162return false;163}164165Boolean support = textMIMESubtypeCharsetSupport.get(subType);166167if (support != null) {168return support;169}170171boolean ret_val = (flavor.getParameter("charset") != null);172textMIMESubtypeCharsetSupport.put(subType, ret_val);173return ret_val;174}175public static boolean doesSubtypeSupportCharset(String subType,176String charset)177{178Boolean support = textMIMESubtypeCharsetSupport.get(subType);179180if (support != null) {181return support;182}183184boolean ret_val = (charset != null);185textMIMESubtypeCharsetSupport.put(subType, ret_val);186return ret_val;187}188189/**190* Returns whether this flavor is a text type which supports the 'charset'191* parameter.192*/193public static boolean isFlavorCharsetTextType(DataFlavor flavor) {194// Although stringFlavor doesn't actually support the charset195// parameter (because its primary MIME type is not "text"), it should196// be treated as though it does. stringFlavor is semantically197// equivalent to "text/plain" data.198if (DataFlavor.stringFlavor.equals(flavor)) {199return true;200}201202if (!"text".equals(flavor.getPrimaryType()) ||203!doesSubtypeSupportCharset(flavor))204{205return false;206}207208Class<?> rep_class = flavor.getRepresentationClass();209210if (flavor.isRepresentationClassReader() ||211String.class.equals(rep_class) ||212flavor.isRepresentationClassCharBuffer() ||213char[].class.equals(rep_class))214{215return true;216}217218if (!(flavor.isRepresentationClassInputStream() ||219flavor.isRepresentationClassByteBuffer() ||220byte[].class.equals(rep_class))) {221return false;222}223224String charset = flavor.getParameter("charset");225226// null equals default encoding which is always supported227return (charset == null) || isEncodingSupported(charset);228}229230/**231* Returns whether this flavor is a text type which does not support the232* 'charset' parameter.233*/234public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) {235if (!"text".equals(flavor.getPrimaryType()) || doesSubtypeSupportCharset(flavor)) {236return false;237}238239return (flavor.isRepresentationClassInputStream() ||240flavor.isRepresentationClassByteBuffer() ||241byte[].class.equals(flavor.getRepresentationClass()));242}243244/**245* If the specified flavor is a text flavor which supports the "charset"246* parameter, then this method returns that parameter, or the default247* charset if no such parameter was specified at construction. For non-text248* DataFlavors, and for non-charset text flavors, this method returns249* {@code null}.250*/251public static String getTextCharset(DataFlavor flavor) {252if (!isFlavorCharsetTextType(flavor)) {253return null;254}255256String encoding = flavor.getParameter("charset");257258return (encoding != null) ? encoding : Charset.defaultCharset().name();259}260261/**262* Determines whether this JRE can both encode and decode text in the263* specified encoding.264*/265private static boolean isEncodingSupported(String encoding) {266if (encoding == null) {267return false;268}269try {270return Charset.isSupported(encoding);271} catch (IllegalCharsetNameException icne) {272return false;273}274}275276/**277* Helper method to compare two objects by their Integer indices in the278* given map. If the map doesn't contain an entry for either of the objects,279* the fallback index will be used for the object instead.280*281* @param indexMap the map which maps objects into Integer indexes282* @param obj1 the first object to be compared283* @param obj2 the second object to be compared284* @param fallbackIndex the Integer to be used as a fallback index285* @return a negative integer, zero, or a positive integer as the first286* object is mapped to a less, equal to, or greater index than the287* second288*/289static <T> int compareIndices(Map<T, Integer> indexMap,290T obj1, T obj2,291Integer fallbackIndex) {292Integer index1 = indexMap.getOrDefault(obj1, fallbackIndex);293Integer index2 = indexMap.getOrDefault(obj2, fallbackIndex);294return index1.compareTo(index2);295}296297/**298* An IndexedComparator which compares two String charsets. The comparison299* follows the rules outlined in DataFlavor.selectBestTextFlavor. In order300* to ensure that non-Unicode, non-ASCII, non-default charsets are sorted301* in alphabetical order, charsets are not automatically converted to their302* canonical forms.303*/304private static class CharsetComparator implements Comparator<String> {305static final CharsetComparator INSTANCE = new CharsetComparator();306307private static final Map<String, Integer> charsets;308309private static final Integer DEFAULT_CHARSET_INDEX = 2;310private static final Integer OTHER_CHARSET_INDEX = 1;311private static final Integer WORST_CHARSET_INDEX = 0;312private static final Integer UNSUPPORTED_CHARSET_INDEX = Integer.MIN_VALUE;313314private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";315316static {317Map<String, Integer> charsetsMap = new HashMap<>(8, 1.0f);318319// we prefer Unicode charsets320charsetsMap.put(canonicalName("UTF-16LE"), 4);321charsetsMap.put(canonicalName("UTF-16BE"), 5);322charsetsMap.put(canonicalName("UTF-8"), 6);323charsetsMap.put(canonicalName("UTF-16"), 7);324325// US-ASCII is the worst charset supported326charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);327328charsetsMap.putIfAbsent(Charset.defaultCharset().name(), DEFAULT_CHARSET_INDEX);329330charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);331332charsets = Collections.unmodifiableMap(charsetsMap);333}334335/**336* Compares charsets. Returns a negative integer, zero, or a positive337* integer as the first charset is worse than, equal to, or better than338* the second.339* <p>340* Charsets are ordered according to the following rules:341* <ul>342* <li>All unsupported charsets are equal</li>343* <li>Any unsupported charset is worse than any supported charset.344* <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and345* "UTF-16LE", are considered best</li>346* <li>After them, platform default charset is selected</li>347* <li>"US-ASCII" is the worst of supported charsets</li>348* <li>For all other supported charsets, the lexicographically less one349* is considered the better</li>350* </ul>351*352* @param charset1 the first charset to be compared353* @param charset2 the second charset to be compared354* @return a negative integer, zero, or a positive integer as the first355* argument is worse, equal to, or better than the second356*/357public int compare(String charset1, String charset2) {358charset1 = getEncoding(charset1);359charset2 = getEncoding(charset2);360361int comp = compareIndices(charsets, charset1, charset2, OTHER_CHARSET_INDEX);362363if (comp == 0) {364return charset2.compareTo(charset1);365}366367return comp;368}369370/**371* Returns encoding for the specified charset according to the following372* rules:373* <ul>374* <li>If the charset is {@code null}, then {@code null} will be375* returned</li>376* <li>Iff the charset specifies an encoding unsupported by this JRE,377* {@code UNSUPPORTED_CHARSET} will be returned</li>378* <li>If the charset specifies an alias name, the corresponding379* canonical name will be returned iff the charset is a known380* Unicode, ASCII, or default charset</li>381* </ul>382*383* @param charset the charset384* @return an encoding for this charset385*/386static String getEncoding(String charset) {387if (charset == null) {388return null;389} else if (!isEncodingSupported(charset)) {390return UNSUPPORTED_CHARSET;391} else {392// Only convert to canonical form if the charset is one393// of the charsets explicitly listed in the known charsets394// map. This will happen only for Unicode, ASCII, or default395// charsets.396String canonicalName = canonicalName(charset);397return (charsets.containsKey(canonicalName))398? canonicalName399: charset;400}401}402}403404/**405* An IndexedComparator which compares two DataFlavors. For text flavors,406* the comparison follows the rules outlined in407* {@link DataFlavor#selectBestTextFlavor selectBestTextFlavor}. For408* non-text flavors, unknown application MIME types are preferred, followed409* by known application/x-java-* MIME types. Unknown application types are410* preferred because if the user provides his own data flavor, it will411* likely be the most descriptive one. For flavors which are otherwise412* equal, the flavors' string representation are compared in the413* alphabetical order.414*/415private static class DataFlavorComparator implements Comparator<DataFlavor> {416417static final DataFlavorComparator INSTANCE = new DataFlavorComparator();418419private static final Map<String, Integer> exactTypes;420private static final Map<String, Integer> primaryTypes;421private static final Map<Class<?>, Integer> nonTextRepresentations;422private static final Map<String, Integer> textTypes;423private static final Map<Class<?>, Integer> decodedTextRepresentations;424private static final Map<Class<?>, Integer> encodedTextRepresentations;425426private static final Integer UNKNOWN_OBJECT_LOSES = Integer.MIN_VALUE;427private static final Integer UNKNOWN_OBJECT_WINS = Integer.MAX_VALUE;428429static {430{431Map<String, Integer> exactTypesMap = new HashMap<>(4, 1.0f);432433// application/x-java-* MIME types434exactTypesMap.put("application/x-java-file-list", 0);435exactTypesMap.put("application/x-java-serialized-object", 1);436exactTypesMap.put("application/x-java-jvm-local-objectref", 2);437exactTypesMap.put("application/x-java-remote-object", 3);438439exactTypes = Collections.unmodifiableMap(exactTypesMap);440}441442{443Map<String, Integer> primaryTypesMap = new HashMap<>(1, 1.0f);444445primaryTypesMap.put("application", 0);446447primaryTypes = Collections.unmodifiableMap(primaryTypesMap);448}449450{451Map<Class<?>, Integer> nonTextRepresentationsMap = new HashMap<>(3, 1.0f);452453nonTextRepresentationsMap.put(java.io.InputStream.class, 0);454nonTextRepresentationsMap.put(java.io.Serializable.class, 1);455456nonTextRepresentationsMap.put(RMI.remoteClass(), 2);457458nonTextRepresentations = Collections.unmodifiableMap(nonTextRepresentationsMap);459}460461{462Map<String, Integer> textTypesMap = new HashMap<>(16, 1.0f);463464// plain text465textTypesMap.put("text/plain", 0);466467// stringFlavor468textTypesMap.put("application/x-java-serialized-object", 1);469470// misc471textTypesMap.put("text/calendar", 2);472textTypesMap.put("text/css", 3);473textTypesMap.put("text/directory", 4);474textTypesMap.put("text/parityfec", 5);475textTypesMap.put("text/rfc822-headers", 6);476textTypesMap.put("text/t140", 7);477textTypesMap.put("text/tab-separated-values", 8);478textTypesMap.put("text/uri-list", 9);479480// enriched481textTypesMap.put("text/richtext", 10);482textTypesMap.put("text/enriched", 11);483textTypesMap.put("text/rtf", 12);484485// markup486textTypesMap.put("text/html", 13);487textTypesMap.put("text/xml", 14);488textTypesMap.put("text/sgml", 15);489490textTypes = Collections.unmodifiableMap(textTypesMap);491}492493{494Map<Class<?>, Integer> decodedTextRepresentationsMap = new HashMap<>(4, 1.0f);495496decodedTextRepresentationsMap.put(char[].class, 0);497decodedTextRepresentationsMap.put(CharBuffer.class, 1);498decodedTextRepresentationsMap.put(String.class, 2);499decodedTextRepresentationsMap.put(Reader.class, 3);500501decodedTextRepresentations =502Collections.unmodifiableMap(decodedTextRepresentationsMap);503}504505{506Map<Class<?>, Integer> encodedTextRepresentationsMap = new HashMap<>(3, 1.0f);507508encodedTextRepresentationsMap.put(byte[].class, 0);509encodedTextRepresentationsMap.put(ByteBuffer.class, 1);510encodedTextRepresentationsMap.put(InputStream.class, 2);511512encodedTextRepresentations =513Collections.unmodifiableMap(encodedTextRepresentationsMap);514}515}516517518public int compare(DataFlavor flavor1, DataFlavor flavor2) {519if (flavor1.equals(flavor2)) {520return 0;521}522523int comp;524525String primaryType1 = flavor1.getPrimaryType();526String subType1 = flavor1.getSubType();527String mimeType1 = primaryType1 + "/" + subType1;528Class<?> class1 = flavor1.getRepresentationClass();529530String primaryType2 = flavor2.getPrimaryType();531String subType2 = flavor2.getSubType();532String mimeType2 = primaryType2 + "/" + subType2;533Class<?> class2 = flavor2.getRepresentationClass();534535if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {536// First, compare MIME types537comp = compareIndices(textTypes, mimeType1, mimeType2, UNKNOWN_OBJECT_LOSES);538if (comp != 0) {539return comp;540}541542// Only need to test one flavor because they both have the543// same MIME type. Also don't need to worry about accidentally544// passing stringFlavor because either545// 1. Both flavors are stringFlavor, in which case the546// equality test at the top of the function succeeded.547// 2. Only one flavor is stringFlavor, in which case the MIME548// type comparison returned a non-zero value.549if (doesSubtypeSupportCharset(flavor1)) {550// Next, prefer the decoded text representations of Reader,551// String, CharBuffer, and [C, in that order.552comp = compareIndices(decodedTextRepresentations, class1,553class2, UNKNOWN_OBJECT_LOSES);554if (comp != 0) {555return comp;556}557558// Next, compare charsets559comp = CharsetComparator.INSTANCE.compare(getTextCharset(flavor1),560getTextCharset(flavor2));561if (comp != 0) {562return comp;563}564}565566// Finally, prefer the encoded text representations of567// InputStream, ByteBuffer, and [B, in that order.568comp = compareIndices(encodedTextRepresentations, class1,569class2, UNKNOWN_OBJECT_LOSES);570if (comp != 0) {571return comp;572}573} else {574// First, prefer text types575if (flavor1.isFlavorTextType()) {576return 1;577}578579if (flavor2.isFlavorTextType()) {580return -1;581}582583// Next, prefer application types.584comp = compareIndices(primaryTypes, primaryType1, primaryType2,585UNKNOWN_OBJECT_LOSES);586if (comp != 0) {587return comp;588}589590// Next, look for application/x-java-* types. Prefer unknown591// MIME types because if the user provides his own data flavor,592// it will likely be the most descriptive one.593comp = compareIndices(exactTypes, mimeType1, mimeType2,594UNKNOWN_OBJECT_WINS);595if (comp != 0) {596return comp;597}598599// Finally, prefer the representation classes of Remote,600// Serializable, and InputStream, in that order.601comp = compareIndices(nonTextRepresentations, class1, class2,602UNKNOWN_OBJECT_LOSES);603if (comp != 0) {604return comp;605}606}607608// The flavours are not equal but still not distinguishable.609// Compare String representations in alphabetical order610return flavor1.getMimeType().compareTo(flavor2.getMimeType());611}612}613614/**615* Given the Map that maps objects to Integer indices and a boolean value,616* this Comparator imposes a direct or reverse order on set of objects.617* <p>618* If the specified boolean value is SELECT_BEST, the Comparator imposes the619* direct index-based order: an object A is greater than an object B if and620* only if the index of A is greater than the index of B. An object that621* doesn't have an associated index is less or equal than any other object.622* <p>623* If the specified boolean value is SELECT_WORST, the Comparator imposes624* the reverse index-based order: an object A is greater than an object B if625* and only if A is less than B with the direct index-based order.626*/627private static class IndexOrderComparator implements Comparator<Long> {628private final Map<Long, Integer> indexMap;629private static final Integer FALLBACK_INDEX = Integer.MIN_VALUE;630631public IndexOrderComparator(Map<Long, Integer> indexMap) {632this.indexMap = indexMap;633}634635public int compare(Long obj1, Long obj2) {636return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);637}638}639640private static class TextFlavorComparator extends DataFlavorComparator {641642static final TextFlavorComparator INSTANCE = new TextFlavorComparator();643644/**645* Compares two {@code DataFlavor} objects. Returns a negative integer,646* zero, or a positive integer as the first {@code DataFlavor} is worse647* than, equal to, or better than the second.648* <p>649* {@code DataFlavor}s are ordered according to the rules outlined for650* {@link DataFlavor#selectBestTextFlavor selectBestTextFlavor}.651*652* @param flavor1 the first {@code DataFlavor} to be compared653* @param flavor2 the second {@code DataFlavor} to be compared654* @return a negative integer, zero, or a positive integer as the first655* argument is worse, equal to, or better than the second656* @throws ClassCastException if either of the arguments is not an657* instance of {@code DataFlavor}658* @throws NullPointerException if either of the arguments is659* {@code null}660* @see DataFlavor#selectBestTextFlavor661*/662public int compare(DataFlavor flavor1, DataFlavor flavor2) {663if (flavor1.isFlavorTextType()) {664if (flavor2.isFlavorTextType()) {665return super.compare(flavor1, flavor2);666} else {667return 1;668}669} else if (flavor2.isFlavorTextType()) {670return -1;671} else {672return 0;673}674}675}676677/**678* A fallback implementation of {@link DesktopDatatransferService} used if679* there is no desktop.680*/681private static final class DefaultDesktopDatatransferService implements DesktopDatatransferService {682static final DesktopDatatransferService INSTANCE = getDesktopService();683684private static DesktopDatatransferService getDesktopService() {685ServiceLoader<DesktopDatatransferService> loader =686ServiceLoader.load(DesktopDatatransferService.class, null);687Iterator<DesktopDatatransferService> iterator = loader.iterator();688if (iterator.hasNext()) {689return iterator.next();690} else {691return new DefaultDesktopDatatransferService();692}693}694695/**696* System singleton FlavorTable. Only used if there is no desktop to697* provide an appropriate FlavorMap.698*/699private volatile FlavorMap flavorMap;700701@Override702public void invokeOnEventThread(Runnable r) {703r.run();704}705706@Override707public String getDefaultUnicodeEncoding() {708return StandardCharsets.UTF_8.name();709}710711@Override712public FlavorMap getFlavorMap(Supplier<FlavorMap> supplier) {713FlavorMap map = flavorMap;714if (map == null) {715synchronized (this) {716map = flavorMap;717if (map == null) {718flavorMap = map = supplier.get();719}720}721}722return map;723}724725@Override726public boolean isDesktopPresent() {727return false;728}729730@Override731public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {732return new LinkedHashSet<>();733}734735@Override736public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {737return new LinkedHashSet<>();738}739740@Override741public void registerTextFlavorProperties(String nat, String charset,742String eoln, String terminators) {743// Not needed if desktop module is absent744}745}746747public static DesktopDatatransferService getDesktopService() {748return DefaultDesktopDatatransferService.INSTANCE;749}750751/**752* A class that provides access to {@code java.rmi.Remote} and753* {@code java.rmi.MarshalledObject} without creating a static dependency.754*/755public static class RMI {756private static final Class<?> remoteClass = getClass("java.rmi.Remote");757private static final Class<?> marshallObjectClass = getClass("java.rmi.MarshalledObject");758private static final Constructor<?> marshallCtor = getConstructor(marshallObjectClass, Object.class);759private static final Method marshallGet = getMethod(marshallObjectClass, "get");760761private static Class<?> getClass(String name) {762try {763return Class.forName(name, true, null);764} catch (ClassNotFoundException e) {765return null;766}767}768769private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {770try {771return (c == null) ? null : c.getDeclaredConstructor(types);772} catch (NoSuchMethodException x) {773throw new AssertionError(x);774}775}776777private static Method getMethod(Class<?> c, String name, Class<?>... types) {778try {779return (c == null) ? null : c.getMethod(name, types);780} catch (NoSuchMethodException e) {781throw new AssertionError(e);782}783}784785/**786* Returns {@code java.rmi.Remote.class} if RMI is present; otherwise787* {@code null}.788*/789static Class<?> remoteClass() {790return remoteClass;791}792793/**794* Returns {@code true} if the given class is java.rmi.Remote.795*/796public static boolean isRemote(Class<?> c) {797return (remoteClass != null) && remoteClass.isAssignableFrom(c);798}799800/**801* Returns a new MarshalledObject containing the serialized802* representation of the given object.803*/804public static Object newMarshalledObject(Object obj) throws IOException {805try {806return marshallCtor == null ? null : marshallCtor.newInstance(obj);807} catch (InstantiationException | IllegalAccessException x) {808throw new AssertionError(x);809} catch (InvocationTargetException x) {810Throwable cause = x.getCause();811if (cause instanceof IOException)812throw (IOException) cause;813throw new AssertionError(x);814}815}816817/**818* Returns a new copy of the contained marshalled object.819*/820public static Object getMarshalledObject(Object obj)821throws IOException, ClassNotFoundException {822try {823return marshallGet == null ? null : marshallGet.invoke(obj);824} catch (IllegalAccessException x) {825throw new AssertionError(x);826} catch (InvocationTargetException x) {827Throwable cause = x.getCause();828if (cause instanceof IOException)829throw (IOException) cause;830if (cause instanceof ClassNotFoundException)831throw (ClassNotFoundException) cause;832throw new AssertionError(x);833}834}835}836}837838839