1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package javax.activation;
19
20 import java.awt.datatransfer.DataFlavor;
21 import java.awt.datatransfer.Transferable;
22 import java.awt.datatransfer.UnsupportedFlavorException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.PipedInputStream;
27 import java.io.PipedOutputStream;
28 import java.net.URL;
29
30 /**
31 * @version $Rev: 376811 $ $Date: 2006-02-10 11:40:13 -0800 (Fri, 10 Feb 2006) $
32 */
33 public class DataHandler implements Transferable {
34 private final DataSource ds;
35 private final DataFlavor flavor;
36
37 private CommandMap commandMap;
38 private DataContentHandler dch;
39
40 public DataHandler(DataSource ds) {
41 this.ds = ds;
42 this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
43 }
44
45 public DataHandler(Object data, String type) {
46 this.ds = new ObjectDataSource(data, type);
47 this.flavor = new ActivationDataFlavor(data.getClass(), null);
48 }
49
50 public DataHandler(URL url) {
51 this.ds = new URLDataSource(url);
52 this.flavor = new ActivationDataFlavor(ds.getContentType(), null);
53 }
54
55 public DataSource getDataSource() {
56 return ds;
57 }
58
59 public String getName() {
60 return ds.getName();
61 }
62
63 public String getContentType() {
64 return ds.getContentType();
65 }
66
67 public InputStream getInputStream() throws IOException {
68 return ds.getInputStream();
69 }
70
71 public void writeTo(OutputStream os) throws IOException {
72 if (ds instanceof ObjectDataSource) {
73 ObjectDataSource ods = (ObjectDataSource) ds;
74 DataContentHandler dch = getDataContentHandler();
75 if (dch == null) {
76 throw new UnsupportedDataTypeException(ods.mimeType);
77 }
78 dch.writeTo(ods.data, ods.mimeType, os);
79 } else {
80 byte[] buffer = new byte[1024];
81 InputStream is = getInputStream();
82 try {
83 int count;
84 while ((count = is.read(buffer)) != -1) {
85 os.write(buffer, 0, count);
86 }
87 } finally {
88 is.close();
89 }
90 }
91 }
92
93 public OutputStream getOutputStream() throws IOException {
94 return ds.getOutputStream();
95 }
96
97 public synchronized DataFlavor[] getTransferDataFlavors() {
98 return getDataContentHandler().getTransferDataFlavors();
99 }
100
101 public boolean isDataFlavorSupported(DataFlavor flavor) {
102 DataFlavor[] flavors = getTransferDataFlavors();
103 for (int i = 0; i < flavors.length; i++) {
104 DataFlavor dataFlavor = flavors[i];
105 if (dataFlavor.equals(flavor)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
113 DataContentHandler dch = getDataContentHandler();
114 if (dch != null) {
115 return dch.getTransferData(flavor, ds);
116 } else if (this.flavor.match(flavor)) {
117 if (ds instanceof ObjectDataSource) {
118 return ((ObjectDataSource) ds).data;
119 } else {
120 return ds.getInputStream();
121 }
122 } else {
123 throw new UnsupportedFlavorException(flavor);
124 }
125 }
126
127 public CommandInfo[] getPreferredCommands() {
128 return getCommandMap().getPreferredCommands(ds.getContentType());
129 }
130
131 public CommandInfo[] getAllCommands() {
132 return getCommandMap().getAllCommands(ds.getContentType());
133 }
134
135 public CommandInfo getCommand(String cmdName) {
136 return getCommandMap().getCommand(ds.getContentType(), cmdName);
137 }
138
139 public Object getContent() throws IOException {
140 if (ds instanceof ObjectDataSource) {
141 return ((ObjectDataSource) ds).data;
142 } else {
143 DataContentHandler dch = getDataContentHandler();
144 if (dch != null) {
145 return dch.getContent(ds);
146 } else {
147 return ds.getInputStream();
148 }
149 }
150 }
151
152 public Object getBean(CommandInfo cmdinfo) {
153 try {
154 return cmdinfo.getCommandObject(this, this.getClass().getClassLoader());
155 } catch (IOException e) {
156 return null;
157 } catch (ClassNotFoundException e) {
158 return null;
159 }
160 }
161
162 /**
163 * A local implementation of DataSouce used to wrap an Object and mime-type.
164 */
165 private class ObjectDataSource implements DataSource {
166 private final Object data;
167 private final String mimeType;
168
169 public ObjectDataSource(Object data, String mimeType) {
170 this.data = data;
171 this.mimeType = mimeType;
172 }
173
174 public String getName() {
175 return null;
176 }
177
178 public String getContentType() {
179 return mimeType;
180 }
181
182 public InputStream getInputStream() throws IOException {
183 final DataContentHandler dch = getDataContentHandler();
184 if (dch == null) {
185 throw new UnsupportedDataTypeException(mimeType);
186 }
187 final PipedInputStream is = new PipedInputStream();
188 final PipedOutputStream os = new PipedOutputStream(is);
189 Thread thread = new Thread("DataHandler Pipe Pump") {
190 public void run() {
191 try {
192 try {
193 dch.writeTo(data, mimeType, os);
194 } finally {
195 os.close();
196 }
197 } catch (IOException e) {
198
199 }
200 }
201 };
202 thread.start();
203 return is;
204 }
205
206 public OutputStream getOutputStream() throws IOException {
207 return null;
208 }
209 }
210
211 public synchronized void setCommandMap(CommandMap commandMap) {
212 this.commandMap = commandMap;
213 this.dch = null;
214 }
215
216 private synchronized CommandMap getCommandMap() {
217 return commandMap != null ? commandMap : CommandMap.getDefaultCommandMap();
218 }
219
220 /**
221 * Search for a DataContentHandler for our mime type.
222 * The search is performed by first checking if a global factory has been set using
223 * {@link #setDataContentHandlerFactory(DataContentHandlerFactory)};
224 * if found then it is called to attempt to create a handler.
225 * If this attempt fails, we then call the command map set using {@link #setCommandMap(CommandMap)}
226 * (or if that has not been set, the default map returned by {@link CommandMap#getDefaultCommandMap()})
227 * to create the handler.
228 *
229 * The resulting handler is cached until the global factory is changed.
230 *
231 * @return
232 */
233 private synchronized DataContentHandler getDataContentHandler() {
234 DataContentHandlerFactory localFactory;
235 synchronized (DataHandler.class) {
236 if (factory != originalFactory) {
237
238 dch = null;
239 originalFactory = factory;
240 }
241 localFactory = originalFactory;
242 }
243 if (dch == null) {
244
245 String mimeType = getMimeType(ds.getContentType());
246 if (localFactory != null) {
247 dch = localFactory.createDataContentHandler(mimeType);
248 }
249 if (dch == null) {
250 if (commandMap != null) {
251 dch = commandMap.createDataContentHandler(mimeType);
252 } else {
253 dch = CommandMap.getDefaultCommandMap().createDataContentHandler(mimeType);
254 }
255 }
256 }
257 return dch;
258 }
259
260 /**
261 * Retrieve the base MIME type from a content type. This parses
262 * the type into its base components, stripping off any parameter
263 * information.
264 *
265 * @param contentType
266 * The content type string.
267 *
268 * @return The MIME type identifier portion of the content type.
269 */
270 private String getMimeType(String contentType) {
271 try {
272 MimeType mimeType = new MimeType(contentType);
273 return mimeType.getBaseType();
274 } catch (MimeTypeParseException e) {
275 }
276 return contentType;
277 }
278
279 /**
280 * This is used to check if the DataContentHandlerFactory has been changed.
281 * This is not specified behaviour but this check is required to make this work like the RI.
282 */
283 private DataContentHandlerFactory originalFactory;
284
285 {
286 synchronized (DataHandler.class) {
287 originalFactory = factory;
288 }
289 }
290
291 private static DataContentHandlerFactory factory;
292
293 /**
294 * Set the DataContentHandlerFactory to use.
295 * If this method has already been called then an Error is raised.
296 *
297 * @param newFactory the new factory
298 * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission
299 */
300 public static synchronized void setDataContentHandlerFactory(DataContentHandlerFactory newFactory) {
301 if (factory != null) {
302 throw new Error("javax.activation.DataHandler.setDataContentHandlerFactory has already been defined");
303 }
304 SecurityManager sm = System.getSecurityManager();
305 if (sm != null) {
306 sm.checkSetFactory();
307 }
308 factory = newFactory;
309 }
310 }