001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package javax.activation; 019 020 import java.awt.datatransfer.DataFlavor; 021 import java.awt.datatransfer.Transferable; 022 import java.awt.datatransfer.UnsupportedFlavorException; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.io.OutputStream; 026 import java.io.PipedInputStream; 027 import java.io.PipedOutputStream; 028 import java.net.URL; 029 030 /** 031 * @version $Rev: 376811 $ $Date: 2006-02-10 11:40:13 -0800 (Fri, 10 Feb 2006) $ 032 */ 033 public class DataHandler implements Transferable { 034 private final DataSource ds; 035 private final DataFlavor flavor; 036 037 private CommandMap commandMap; 038 private DataContentHandler dch; 039 040 public DataHandler(DataSource ds) { 041 this.ds = ds; 042 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 043 } 044 045 public DataHandler(Object data, String type) { 046 this.ds = new ObjectDataSource(data, type); 047 this.flavor = new ActivationDataFlavor(data.getClass(), null); 048 } 049 050 public DataHandler(URL url) { 051 this.ds = new URLDataSource(url); 052 this.flavor = new ActivationDataFlavor(ds.getContentType(), null); 053 } 054 055 public DataSource getDataSource() { 056 return ds; 057 } 058 059 public String getName() { 060 return ds.getName(); 061 } 062 063 public String getContentType() { 064 return ds.getContentType(); 065 } 066 067 public InputStream getInputStream() throws IOException { 068 return ds.getInputStream(); 069 } 070 071 public void writeTo(OutputStream os) throws IOException { 072 if (ds instanceof ObjectDataSource) { 073 ObjectDataSource ods = (ObjectDataSource) ds; 074 DataContentHandler dch = getDataContentHandler(); 075 if (dch == null) { 076 throw new UnsupportedDataTypeException(ods.mimeType); 077 } 078 dch.writeTo(ods.data, ods.mimeType, os); 079 } else { 080 byte[] buffer = new byte[1024]; 081 InputStream is = getInputStream(); 082 try { 083 int count; 084 while ((count = is.read(buffer)) != -1) { 085 os.write(buffer, 0, count); 086 } 087 } finally { 088 is.close(); 089 } 090 } 091 } 092 093 public OutputStream getOutputStream() throws IOException { 094 return ds.getOutputStream(); 095 } 096 097 public synchronized DataFlavor[] getTransferDataFlavors() { 098 return getDataContentHandler().getTransferDataFlavors(); 099 } 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 // ignore, per spec - doh! 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 // setDCHF was called - clear our cached copy of the DCH and DCHF 238 dch = null; 239 originalFactory = factory; 240 } 241 localFactory = originalFactory; 242 } 243 if (dch == null) { 244 // get the main mime-type portion of the content. 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 }