001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package javax.activation; 021 022 import java.awt.datatransfer.DataFlavor; 023 import java.awt.datatransfer.Transferable; 024 import java.awt.datatransfer.UnsupportedFlavorException; 025 import java.io.IOException; 026 import java.io.InputStream; 027 import java.io.OutputStream; 028 import java.io.PipedInputStream; 029 import java.io.PipedOutputStream; 030 import java.net.URL; 031 032 /** 033 * @version $Rev: 467553 $ $Date: 2006-10-25 06:01:51 +0200 (Mi, 25. Okt 2006) $ 034 */ 035 public class DataHandler implements Transferable { 036 private final DataSource ds; 037 private final DataFlavor flavor; 038 039 private CommandMap commandMap; 040 private DataContentHandler dch; 041 042 public DataHandler(DataSource ds) { 043 this.ds = ds; 044 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 045 } 046 047 public DataHandler(Object data, String type) { 048 this.ds = new ObjectDataSource(data, type); 049 this.flavor = new ActivationDataFlavor(data.getClass(), null); 050 } 051 052 public DataHandler(URL url) { 053 this.ds = new URLDataSource(url); 054 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 055 } 056 057 public DataSource getDataSource() { 058 return ds; 059 } 060 061 public String getName() { 062 return ds.getName(); 063 } 064 065 public String getContentType() { 066 return ds.getContentType(); 067 } 068 069 public InputStream getInputStream() throws IOException { 070 return ds.getInputStream(); 071 } 072 073 public void writeTo(OutputStream os) throws IOException { 074 if (ds instanceof ObjectDataSource) { 075 ObjectDataSource ods = (ObjectDataSource) ds; 076 DataContentHandler dch = getDataContentHandler(); 077 if (dch == null) { 078 throw new UnsupportedDataTypeException(ods.mimeType); 079 } 080 dch.writeTo(ods.data, ods.mimeType, os); 081 } else { 082 byte[] buffer = new byte[1024]; 083 InputStream is = getInputStream(); 084 try { 085 int count; 086 while ((count = is.read(buffer)) != -1) { 087 os.write(buffer, 0, count); 088 } 089 } finally { 090 is.close(); 091 } 092 } 093 } 094 095 public OutputStream getOutputStream() throws IOException { 096 return ds.getOutputStream(); 097 } 098 099 public synchronized DataFlavor[] getTransferDataFlavors() { 100 return getDataContentHandler().getTransferDataFlavors(); 101 } 102 103 public boolean isDataFlavorSupported(DataFlavor flavor) { 104 DataFlavor[] flavors = getTransferDataFlavors(); 105 for (int i = 0; i < flavors.length; i++) { 106 DataFlavor dataFlavor = flavors[i]; 107 if (dataFlavor.equals(flavor)) { 108 return true; 109 } 110 } 111 return false; 112 } 113 114 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 115 DataContentHandler dch = getDataContentHandler(); 116 if (dch != null) { 117 return dch.getTransferData(flavor, ds); 118 } else if (this.flavor.match(flavor)) { 119 if (ds instanceof ObjectDataSource) { 120 return ((ObjectDataSource) ds).data; 121 } else { 122 return ds.getInputStream(); 123 } 124 } else { 125 throw new UnsupportedFlavorException(flavor); 126 } 127 } 128 129 public CommandInfo[] getPreferredCommands() { 130 return getCommandMap().getPreferredCommands(ds.getContentType()); 131 } 132 133 public CommandInfo[] getAllCommands() { 134 return getCommandMap().getAllCommands(ds.getContentType()); 135 } 136 137 public CommandInfo getCommand(String cmdName) { 138 return getCommandMap().getCommand(ds.getContentType(), cmdName); 139 } 140 141 public Object getContent() throws IOException { 142 if (ds instanceof ObjectDataSource) { 143 return ((ObjectDataSource) ds).data; 144 } else { 145 DataContentHandler dch = getDataContentHandler(); 146 if (dch != null) { 147 return dch.getContent(ds); 148 } else { 149 return ds.getInputStream(); 150 } 151 } 152 } 153 154 public Object getBean(CommandInfo cmdinfo) { 155 try { 156 return cmdinfo.getCommandObject(this, this.getClass().getClassLoader()); 157 } catch (IOException e) { 158 return null; 159 } catch (ClassNotFoundException e) { 160 return null; 161 } 162 } 163 164 /** 165 * A local implementation of DataSouce used to wrap an Object and mime-type. 166 */ 167 private class ObjectDataSource implements DataSource { 168 private final Object data; 169 private final String mimeType; 170 171 public ObjectDataSource(Object data, String mimeType) { 172 this.data = data; 173 this.mimeType = mimeType; 174 } 175 176 public String getName() { 177 return null; 178 } 179 180 public String getContentType() { 181 return mimeType; 182 } 183 184 public InputStream getInputStream() throws IOException { 185 final DataContentHandler dch = getDataContentHandler(); 186 if (dch == null) { 187 throw new UnsupportedDataTypeException(mimeType); 188 } 189 final PipedInputStream is = new PipedInputStream(); 190 final PipedOutputStream os = new PipedOutputStream(is); 191 Thread thread = new Thread("DataHandler Pipe Pump") { 192 public void run() { 193 try { 194 try { 195 dch.writeTo(data, mimeType, os); 196 } finally { 197 os.close(); 198 } 199 } catch (IOException e) { 200 // ignore, per spec - doh! 201 } 202 } 203 }; 204 thread.start(); 205 return is; 206 } 207 208 public OutputStream getOutputStream() throws IOException { 209 return null; 210 } 211 } 212 213 public synchronized void setCommandMap(CommandMap commandMap) { 214 this.commandMap = commandMap; 215 this.dch = null; 216 } 217 218 private synchronized CommandMap getCommandMap() { 219 return commandMap != null ? commandMap : CommandMap.getDefaultCommandMap(); 220 } 221 222 /** 223 * Search for a DataContentHandler for our mime type. 224 * The search is performed by first checking if a global factory has been set using 225 * {@link #setDataContentHandlerFactory(DataContentHandlerFactory)}; 226 * if found then it is called to attempt to create a handler. 227 * If this attempt fails, we then call the command map set using {@link #setCommandMap(CommandMap)} 228 * (or if that has not been set, the default map returned by {@link CommandMap#getDefaultCommandMap()}) 229 * to create the handler. 230 * 231 * The resulting handler is cached until the global factory is changed. 232 * 233 * @return 234 */ 235 private synchronized DataContentHandler getDataContentHandler() { 236 DataContentHandlerFactory localFactory; 237 synchronized (DataHandler.class) { 238 if (factory != originalFactory) { 239 // setDCHF was called - clear our cached copy of the DCH and DCHF 240 dch = null; 241 originalFactory = factory; 242 } 243 localFactory = originalFactory; 244 } 245 if (dch == null) { 246 // get the main mime-type portion of the content. 247 String mimeType = getMimeType(ds.getContentType()); 248 if (localFactory != null) { 249 dch = localFactory.createDataContentHandler(mimeType); 250 } 251 if (dch == null) { 252 if (commandMap != null) { 253 dch = commandMap.createDataContentHandler(mimeType); 254 } else { 255 dch = CommandMap.getDefaultCommandMap().createDataContentHandler(mimeType); 256 } 257 } 258 } 259 return dch; 260 } 261 262 /** 263 * Retrieve the base MIME type from a content type. This parses 264 * the type into its base components, stripping off any parameter 265 * information. 266 * 267 * @param contentType 268 * The content type string. 269 * 270 * @return The MIME type identifier portion of the content type. 271 */ 272 private String getMimeType(String contentType) { 273 try { 274 MimeType mimeType = new MimeType(contentType); 275 return mimeType.getBaseType(); 276 } catch (MimeTypeParseException e) { 277 } 278 return contentType; 279 } 280 281 /** 282 * This is used to check if the DataContentHandlerFactory has been changed. 283 * This is not specified behaviour but this check is required to make this work like the RI. 284 */ 285 private DataContentHandlerFactory originalFactory; 286 287 { 288 synchronized (DataHandler.class) { 289 originalFactory = factory; 290 } 291 } 292 293 private static DataContentHandlerFactory factory; 294 295 /** 296 * Set the DataContentHandlerFactory to use. 297 * If this method has already been called then an Error is raised. 298 * 299 * @param newFactory the new factory 300 * @throws SecurityException if the caller does not have "SetFactory" RuntimePermission 301 */ 302 public static synchronized void setDataContentHandlerFactory(DataContentHandlerFactory newFactory) { 303 if (factory != null) { 304 throw new Error("javax.activation.DataHandler.setDataContentHandlerFactory has already been defined"); 305 } 306 SecurityManager sm = System.getSecurityManager(); 307 if (sm != null) { 308 sm.checkSetFactory(); 309 } 310 factory = newFactory; 311 } 312 }