1/*
2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 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
26package sun.awt.datatransfer;
27
28import java.awt.datatransfer.DataFlavor;
29import java.awt.datatransfer.Transferable;
30import java.awt.datatransfer.UnsupportedFlavorException;
31import java.io.ByteArrayInputStream;
32import java.io.ByteArrayOutputStream;
33import java.io.InputStream;
34import java.io.IOException;
35import java.io.ObjectInputStream;
36import java.io.ObjectOutputStream;
37import java.io.ObjectStreamClass;
38import java.io.OutputStream;
39import java.lang.reflect.Modifier;
40import java.lang.reflect.Proxy;
41import java.security.AccessController;
42import java.security.PrivilegedAction;
43import java.util.HashMap;
44import java.util.HashSet;
45import java.util.Map;
46import java.util.Set;
47
48
49/**
50 * Proxies for another Transferable so that Serializable objects are never
51 * returned directly by DnD or the Clipboard. Instead, a new instance of the
52 * object is returned.
53 *
54 * @author Lawrence P.G. Cable
55 * @author David Mendenhall
56 *
57 * @since 1.4
58 */
59public class TransferableProxy implements Transferable {
60    public TransferableProxy(Transferable t, boolean local) {
61        transferable = t;
62        isLocal = local;
63    }
64    public DataFlavor[] getTransferDataFlavors() {
65        return transferable.getTransferDataFlavors();
66    }
67    public boolean isDataFlavorSupported(DataFlavor flavor) {
68        return transferable.isDataFlavorSupported(flavor);
69    }
70    public Object getTransferData(DataFlavor df)
71        throws UnsupportedFlavorException, IOException
72    {
73        Object data = transferable.getTransferData(df);
74
75        // If the data is a Serializable object, then create a new instance
76        // before returning it. This insulates applications sharing DnD and
77        // Clipboard data from each other.
78        if (data != null && isLocal && df.isFlavorSerializedObjectType()) {
79            ByteArrayOutputStream baos = new ByteArrayOutputStream();
80
81            ClassLoaderObjectOutputStream oos =
82                new ClassLoaderObjectOutputStream(baos);
83            oos.writeObject(data);
84
85            ByteArrayInputStream bais =
86                new ByteArrayInputStream(baos.toByteArray());
87
88            try {
89                ClassLoaderObjectInputStream ois =
90                    new ClassLoaderObjectInputStream(bais,
91                                                     oos.getClassLoaderMap());
92                data = ois.readObject();
93            } catch (ClassNotFoundException cnfe) {
94                throw (IOException)new IOException().initCause(cnfe);
95            }
96        }
97
98        return data;
99    }
100
101    protected final Transferable transferable;
102    protected final boolean isLocal;
103}
104
105final class ClassLoaderObjectOutputStream extends ObjectOutputStream {
106    private final Map<Set<String>, ClassLoader> map =
107        new HashMap<Set<String>, ClassLoader>();
108
109    ClassLoaderObjectOutputStream(OutputStream os) throws IOException {
110        super(os);
111    }
112
113    protected void annotateClass(final Class<?> cl) throws IOException {
114        ClassLoader classLoader = AccessController.doPrivileged(
115            new PrivilegedAction<ClassLoader>() {
116                public ClassLoader run() {
117                    return cl.getClassLoader();
118                }
119            });
120
121        Set<String> s = new HashSet<String>(1);
122        s.add(cl.getName());
123
124        map.put(s, classLoader);
125    }
126    protected void annotateProxyClass(final Class<?> cl) throws IOException {
127        ClassLoader classLoader = AccessController.doPrivileged(
128            new PrivilegedAction<ClassLoader>() {
129                public ClassLoader run() {
130                    return cl.getClassLoader();
131                }
132            });
133
134        Class<?>[] interfaces = cl.getInterfaces();
135        Set<String> s = new HashSet<String>(interfaces.length);
136        for (int i = 0; i < interfaces.length; i++) {
137            s.add(interfaces[i].getName());
138        }
139
140        map.put(s, classLoader);
141    }
142
143    Map<Set<String>, ClassLoader> getClassLoaderMap() {
144        return new HashMap<>(map);
145    }
146}
147
148final class ClassLoaderObjectInputStream extends ObjectInputStream {
149    private final Map<Set<String>, ClassLoader> map;
150
151    ClassLoaderObjectInputStream(InputStream is,
152                                 Map<Set<String>, ClassLoader> map)
153      throws IOException {
154        super(is);
155        if (map == null) {
156            throw new NullPointerException("Null map");
157        }
158        this.map = map;
159    }
160
161    protected Class<?> resolveClass(ObjectStreamClass classDesc)
162      throws IOException, ClassNotFoundException {
163        String className = classDesc.getName();
164
165        Set<String> s = new HashSet<String>(1);
166        s.add(className);
167
168        ClassLoader classLoader = map.get(s);
169        if (classLoader != null) {
170            return Class.forName(className, false, classLoader);
171        } else {
172            return super.resolveClass(classDesc);
173        }
174    }
175
176    protected Class<?> resolveProxyClass(String[] interfaces)
177      throws IOException, ClassNotFoundException {
178
179        Set<String> s = new HashSet<String>(interfaces.length);
180        for (int i = 0; i < interfaces.length; i++) {
181            s.add(interfaces[i]);
182        }
183
184        ClassLoader classLoader = map.get(s);
185        if (classLoader == null) {
186            return super.resolveProxyClass(interfaces);
187        }
188
189        // The code below is mostly copied from the superclass.
190        ClassLoader nonPublicLoader = null;
191        boolean hasNonPublicInterface = false;
192
193        // define proxy in class loader of non-public interface(s), if any
194        Class<?>[] classObjs = new Class<?>[interfaces.length];
195        for (int i = 0; i < interfaces.length; i++) {
196            Class<?> cl = Class.forName(interfaces[i], false, classLoader);
197            if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
198                if (hasNonPublicInterface) {
199                    if (nonPublicLoader != cl.getClassLoader()) {
200                        throw new IllegalAccessError(
201                            "conflicting non-public interface class loaders");
202                    }
203                } else {
204                    nonPublicLoader = cl.getClassLoader();
205                    hasNonPublicInterface = true;
206                }
207            }
208            classObjs[i] = cl;
209        }
210        try {
211            @SuppressWarnings("deprecation")
212            Class<?> proxyClass = Proxy.getProxyClass(hasNonPublicInterface ?
213                                                          nonPublicLoader : classLoader,
214                                                      classObjs);
215            return proxyClass;
216        } catch (IllegalArgumentException e) {
217            throw new ClassNotFoundException(null, e);
218        }
219    }
220}
221