1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaf_classes/javax/activation/DataHandler.java Tue Mar 06 16:09:35 2012 -0800 1.3 @@ -0,0 +1,894 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package javax.activation; 1.30 + 1.31 +import java.io.InputStream; 1.32 +import java.io.IOException; 1.33 +import java.io.OutputStream; 1.34 +import java.io.PipedInputStream; 1.35 +import java.io.PipedOutputStream; 1.36 +import java.io.OutputStreamWriter; 1.37 +import java.net.URL; 1.38 +import java.awt.datatransfer.Transferable; 1.39 +import java.awt.datatransfer.DataFlavor; 1.40 +import java.awt.datatransfer.UnsupportedFlavorException; 1.41 + 1.42 +/** 1.43 + * The DataHandler class provides a consistent interface to data 1.44 + * available in many different sources and formats. 1.45 + * It manages simple stream to string conversions and related operations 1.46 + * using DataContentHandlers. 1.47 + * It provides access to commands that can operate on the data. 1.48 + * The commands are found using a CommandMap. <p> 1.49 + * 1.50 + * <b>DataHandler and the Transferable Interface</b><p> 1.51 + * DataHandler implements the Transferable interface so that data can 1.52 + * be used in AWT data transfer operations, such as cut and paste and 1.53 + * drag and drop. The implementation of the Transferable interface 1.54 + * relies on the availability of an installed DataContentHandler 1.55 + * object corresponding to the MIME type of the data represented in 1.56 + * the specific instance of the DataHandler.<p> 1.57 + * 1.58 + * <b>DataHandler and CommandMaps</b><p> 1.59 + * The DataHandler keeps track of the current CommandMap that it uses to 1.60 + * service requests for commands (<code>getCommand</code>, 1.61 + * <code>getAllCommands</code>, <code>getPreferredCommands</code>). 1.62 + * Each instance of a DataHandler may have a CommandMap associated with 1.63 + * it using the <code>setCommandMap</code> method. If a CommandMap was 1.64 + * not set, DataHandler calls the <code>getDefaultCommandMap</code> 1.65 + * method in CommandMap and uses the value it returns. See 1.66 + * <i>CommandMap</i> for more information. <p> 1.67 + * 1.68 + * <b>DataHandler and URLs</b><p> 1.69 + * The current DataHandler implementation creates a private 1.70 + * instance of URLDataSource when it is constructed with a URL. 1.71 + * 1.72 + * @see javax.activation.CommandMap 1.73 + * @see javax.activation.DataContentHandler 1.74 + * @see javax.activation.DataSource 1.75 + * @see javax.activation.URLDataSource 1.76 + * 1.77 + * @since 1.6 1.78 + */ 1.79 + 1.80 +public class DataHandler implements Transferable { 1.81 + 1.82 + // Use the datasource to indicate whether we were started via the 1.83 + // DataSource constructor or the object constructor. 1.84 + private DataSource dataSource = null; 1.85 + private DataSource objDataSource = null; 1.86 + 1.87 + // The Object and mimetype from the constructor (if passed in). 1.88 + // object remains null if it was instantiated with a 1.89 + // DataSource. 1.90 + private Object object = null; 1.91 + private String objectMimeType = null; 1.92 + 1.93 + // Keep track of the CommandMap 1.94 + private CommandMap currentCommandMap = null; 1.95 + 1.96 + // our transfer flavors 1.97 + private static final DataFlavor emptyFlavors[] = new DataFlavor[0]; 1.98 + private DataFlavor transferFlavors[] = emptyFlavors; 1.99 + 1.100 + // our DataContentHandler 1.101 + private DataContentHandler dataContentHandler = null; 1.102 + private DataContentHandler factoryDCH = null; 1.103 + 1.104 + // our DataContentHandlerFactory 1.105 + private static DataContentHandlerFactory factory = null; 1.106 + private DataContentHandlerFactory oldFactory = null; 1.107 + // the short representation of the ContentType (sans params) 1.108 + private String shortType = null; 1.109 + 1.110 + /** 1.111 + * Create a <code>DataHandler</code> instance referencing the 1.112 + * specified DataSource. The data exists in a byte stream form. 1.113 + * The DataSource will provide an InputStream to access the data. 1.114 + * 1.115 + * @param ds the DataSource 1.116 + */ 1.117 + public DataHandler(DataSource ds) { 1.118 + // save a reference to the incoming DS 1.119 + dataSource = ds; 1.120 + oldFactory = factory; // keep track of the factory 1.121 + } 1.122 + 1.123 + /** 1.124 + * Create a <code>DataHandler</code> instance representing an object 1.125 + * of this MIME type. This constructor is 1.126 + * used when the application already has an in-memory representation 1.127 + * of the data in the form of a Java Object. 1.128 + * 1.129 + * @param obj the Java Object 1.130 + * @param mimeType the MIME type of the object 1.131 + */ 1.132 + public DataHandler(Object obj, String mimeType) { 1.133 + object = obj; 1.134 + objectMimeType = mimeType; 1.135 + oldFactory = factory; // keep track of the factory 1.136 + } 1.137 + 1.138 + /** 1.139 + * Create a <code>DataHandler</code> instance referencing a URL. 1.140 + * The DataHandler internally creates a <code>URLDataSource</code> 1.141 + * instance to represent the URL. 1.142 + * 1.143 + * @param url a URL object 1.144 + */ 1.145 + public DataHandler(URL url) { 1.146 + dataSource = new URLDataSource(url); 1.147 + oldFactory = factory; // keep track of the factory 1.148 + } 1.149 + 1.150 + /** 1.151 + * Return the CommandMap for this instance of DataHandler. 1.152 + */ 1.153 + private synchronized CommandMap getCommandMap() { 1.154 + if (currentCommandMap != null) 1.155 + return currentCommandMap; 1.156 + else 1.157 + return CommandMap.getDefaultCommandMap(); 1.158 + } 1.159 + 1.160 + /** 1.161 + * Return the DataSource associated with this instance 1.162 + * of DataHandler. 1.163 + * <p> 1.164 + * For DataHandlers that have been instantiated with a DataSource, 1.165 + * this method returns the DataSource that was used to create the 1.166 + * DataHandler object. In other cases the DataHandler 1.167 + * constructs a DataSource from the data used to construct 1.168 + * the DataHandler. DataSources created for DataHandlers <b>not</b> 1.169 + * instantiated with a DataSource are cached for performance 1.170 + * reasons. 1.171 + * 1.172 + * @return a valid DataSource object for this DataHandler 1.173 + */ 1.174 + public DataSource getDataSource() { 1.175 + if (dataSource == null) { 1.176 + // create one on the fly 1.177 + if (objDataSource == null) 1.178 + objDataSource = new DataHandlerDataSource(this); 1.179 + return objDataSource; 1.180 + } 1.181 + return dataSource; 1.182 + } 1.183 + 1.184 + /** 1.185 + * Return the name of the data object. If this DataHandler 1.186 + * was created with a DataSource, this method calls through 1.187 + * to the <code>DataSource.getName</code> method, otherwise it 1.188 + * returns <i>null</i>. 1.189 + * 1.190 + * @return the name of the object 1.191 + */ 1.192 + public String getName() { 1.193 + if (dataSource != null) 1.194 + return dataSource.getName(); 1.195 + else 1.196 + return null; 1.197 + } 1.198 + 1.199 + /** 1.200 + * Return the MIME type of this object as retrieved from 1.201 + * the source object. Note that this is the <i>full</i> 1.202 + * type with parameters. 1.203 + * 1.204 + * @return the MIME type 1.205 + */ 1.206 + public String getContentType() { 1.207 + if (dataSource != null) // data source case 1.208 + return dataSource.getContentType(); 1.209 + else 1.210 + return objectMimeType; // obj/type case 1.211 + } 1.212 + 1.213 + /** 1.214 + * Get the InputStream for this object. <p> 1.215 + * 1.216 + * For DataHandlers instantiated with a DataSource, the DataHandler 1.217 + * calls the <code>DataSource.getInputStream</code> method and 1.218 + * returns the result to the caller. 1.219 + * <p> 1.220 + * For DataHandlers instantiated with an Object, the DataHandler 1.221 + * first attempts to find a DataContentHandler for the Object. If 1.222 + * the DataHandler can not find a DataContentHandler for this MIME 1.223 + * type, it throws an UnsupportedDataTypeException. If it is 1.224 + * successful, it creates a pipe and a thread. The thread uses the 1.225 + * DataContentHandler's <code>writeTo</code> method to write the 1.226 + * stream data into one end of the pipe. The other end of the pipe 1.227 + * is returned to the caller. Because a thread is created to copy 1.228 + * the data, IOExceptions that may occur during the copy can not be 1.229 + * propagated back to the caller. The result is an empty stream.<p> 1.230 + * 1.231 + * @return the InputStream representing this data 1.232 + * @exception IOException if an I/O error occurs 1.233 + * 1.234 + * @see javax.activation.DataContentHandler#writeTo 1.235 + * @see javax.activation.UnsupportedDataTypeException 1.236 + */ 1.237 + public InputStream getInputStream() throws IOException { 1.238 + InputStream ins = null; 1.239 + 1.240 + if (dataSource != null) { 1.241 + ins = dataSource.getInputStream(); 1.242 + } else { 1.243 + DataContentHandler dch = getDataContentHandler(); 1.244 + // we won't even try if we can't get a dch 1.245 + if (dch == null) 1.246 + throw new UnsupportedDataTypeException( 1.247 + "no DCH for MIME type " + getBaseType()); 1.248 + 1.249 + if (dch instanceof ObjectDataContentHandler) { 1.250 + if (((ObjectDataContentHandler)dch).getDCH() == null) 1.251 + throw new UnsupportedDataTypeException( 1.252 + "no object DCH for MIME type " + getBaseType()); 1.253 + } 1.254 + // there is none but the default^^^^^^^^^^^^^^^^ 1.255 + final DataContentHandler fdch = dch; 1.256 + 1.257 + // from bill s. 1.258 + // ce n'est pas une pipe! 1.259 + // 1.260 + // NOTE: This block of code needs to throw exceptions, but 1.261 + // can't because it is in another thread!!! ARG! 1.262 + // 1.263 + final PipedOutputStream pos = new PipedOutputStream(); 1.264 + PipedInputStream pin = new PipedInputStream(pos); 1.265 + new Thread( 1.266 + new Runnable() { 1.267 + public void run() { 1.268 + try { 1.269 + fdch.writeTo(object, objectMimeType, pos); 1.270 + } catch (IOException e) { 1.271 + 1.272 + } finally { 1.273 + try { 1.274 + pos.close(); 1.275 + } catch (IOException ie) { } 1.276 + } 1.277 + } 1.278 + }, 1.279 + "DataHandler.getInputStream").start(); 1.280 + ins = pin; 1.281 + } 1.282 + 1.283 + return ins; 1.284 + } 1.285 + 1.286 + /** 1.287 + * Write the data to an <code>OutputStream</code>.<p> 1.288 + * 1.289 + * If the DataHandler was created with a DataSource, writeTo 1.290 + * retrieves the InputStream and copies the bytes from the 1.291 + * InputStream to the OutputStream passed in. 1.292 + * <p> 1.293 + * If the DataHandler was created with an object, writeTo 1.294 + * retrieves the DataContentHandler for the object's type. 1.295 + * If the DataContentHandler was found, it calls the 1.296 + * <code>writeTo</code> method on the <code>DataContentHandler</code>. 1.297 + * 1.298 + * @param os the OutputStream to write to 1.299 + * @exception IOException if an I/O error occurs 1.300 + */ 1.301 + public void writeTo(OutputStream os) throws IOException { 1.302 + // for the DataSource case 1.303 + if (dataSource != null) { 1.304 + InputStream is = null; 1.305 + byte data[] = new byte[8*1024]; 1.306 + int bytes_read; 1.307 + 1.308 + is = dataSource.getInputStream(); 1.309 + 1.310 + try { 1.311 + while ((bytes_read = is.read(data)) > 0) { 1.312 + os.write(data, 0, bytes_read); 1.313 + } 1.314 + } finally { 1.315 + is.close(); 1.316 + is = null; 1.317 + } 1.318 + } else { // for the Object case 1.319 + DataContentHandler dch = getDataContentHandler(); 1.320 + dch.writeTo(object, objectMimeType, os); 1.321 + } 1.322 + } 1.323 + 1.324 + /** 1.325 + * Get an OutputStream for this DataHandler to allow overwriting 1.326 + * the underlying data. 1.327 + * If the DataHandler was created with a DataSource, the 1.328 + * DataSource's <code>getOutputStream</code> method is called. 1.329 + * Otherwise, <code>null</code> is returned. 1.330 + * 1.331 + * @return the OutputStream 1.332 + * 1.333 + * @see javax.activation.DataSource#getOutputStream 1.334 + * @see javax.activation.URLDataSource 1.335 + */ 1.336 + public OutputStream getOutputStream() throws IOException { 1.337 + if (dataSource != null) 1.338 + return dataSource.getOutputStream(); 1.339 + else 1.340 + return null; 1.341 + } 1.342 + 1.343 + /** 1.344 + * Return the DataFlavors in which this data is available. <p> 1.345 + * 1.346 + * Returns an array of DataFlavor objects indicating the flavors 1.347 + * the data can be provided in. The array is usually ordered 1.348 + * according to preference for providing the data, from most 1.349 + * richly descriptive to least richly descriptive.<p> 1.350 + * 1.351 + * The DataHandler attempts to find a DataContentHandler that 1.352 + * corresponds to the MIME type of the data. If one is located, 1.353 + * the DataHandler calls the DataContentHandler's 1.354 + * <code>getTransferDataFlavors</code> method. <p> 1.355 + * 1.356 + * If a DataContentHandler can <i>not</i> be located, and if the 1.357 + * DataHandler was created with a DataSource (or URL), one 1.358 + * DataFlavor is returned that represents this object's MIME type 1.359 + * and the <code>java.io.InputStream</code> class. If the 1.360 + * DataHandler was created with an object and a MIME type, 1.361 + * getTransferDataFlavors returns one DataFlavor that represents 1.362 + * this object's MIME type and the object's class. 1.363 + * 1.364 + * @return an array of data flavors in which this data can be transferred 1.365 + * @see javax.activation.DataContentHandler#getTransferDataFlavors 1.366 + */ 1.367 + public synchronized DataFlavor[] getTransferDataFlavors() { 1.368 + if (factory != oldFactory) // if the factory has changed, clear cache 1.369 + transferFlavors = emptyFlavors; 1.370 + 1.371 + // if it's not set, set it... 1.372 + if (transferFlavors == emptyFlavors) 1.373 + transferFlavors = getDataContentHandler().getTransferDataFlavors(); 1.374 + return transferFlavors; 1.375 + } 1.376 + 1.377 + /** 1.378 + * Returns whether the specified data flavor is supported 1.379 + * for this object.<p> 1.380 + * 1.381 + * This method iterates through the DataFlavors returned from 1.382 + * <code>getTransferDataFlavors</code>, comparing each with 1.383 + * the specified flavor. 1.384 + * 1.385 + * @param flavor the requested flavor for the data 1.386 + * @return true if the data flavor is supported 1.387 + * @see javax.activation.DataHandler#getTransferDataFlavors 1.388 + */ 1.389 + public boolean isDataFlavorSupported(DataFlavor flavor) { 1.390 + DataFlavor[] lFlavors = getTransferDataFlavors(); 1.391 + 1.392 + for (int i = 0; i < lFlavors.length; i++) { 1.393 + if (lFlavors[i].equals(flavor)) 1.394 + return true; 1.395 + } 1.396 + return false; 1.397 + } 1.398 + 1.399 + /** 1.400 + * Returns an object that represents the data to be 1.401 + * transferred. The class of the object returned is defined by the 1.402 + * representation class of the data flavor.<p> 1.403 + * 1.404 + * <b>For DataHandler's created with DataSources or URLs:</b><p> 1.405 + * 1.406 + * The DataHandler attempts to locate a DataContentHandler 1.407 + * for this MIME type. If one is found, the passed in DataFlavor 1.408 + * and the type of the data are passed to its <code>getTransferData</code> 1.409 + * method. If the DataHandler fails to locate a DataContentHandler 1.410 + * and the flavor specifies this object's MIME type and the 1.411 + * <code>java.io.InputStream</code> class, this object's InputStream 1.412 + * is returned. 1.413 + * Otherwise it throws an UnsupportedFlavorException. <p> 1.414 + * 1.415 + * <b>For DataHandler's created with Objects:</b><p> 1.416 + * 1.417 + * The DataHandler attempts to locate a DataContentHandler 1.418 + * for this MIME type. If one is found, the passed in DataFlavor 1.419 + * and the type of the data are passed to its getTransferData 1.420 + * method. If the DataHandler fails to locate a DataContentHandler 1.421 + * and the flavor specifies this object's MIME type and its class, 1.422 + * this DataHandler's referenced object is returned. 1.423 + * Otherwise it throws an UnsupportedFlavorException. 1.424 + * 1.425 + * @param flavor the requested flavor for the data 1.426 + * @return the object 1.427 + * @exception UnsupportedFlavorException if the data could not be 1.428 + * converted to the requested flavor 1.429 + * @exception IOException if an I/O error occurs 1.430 + * @see javax.activation.ActivationDataFlavor 1.431 + */ 1.432 + public Object getTransferData(DataFlavor flavor) 1.433 + throws UnsupportedFlavorException, IOException { 1.434 + return getDataContentHandler().getTransferData(flavor, dataSource); 1.435 + } 1.436 + 1.437 + /** 1.438 + * Set the CommandMap for use by this DataHandler. 1.439 + * Setting it to <code>null</code> causes the CommandMap to revert 1.440 + * to the CommandMap returned by the 1.441 + * <code>CommandMap.getDefaultCommandMap</code> method. 1.442 + * Changing the CommandMap, or setting it to <code>null</code>, 1.443 + * clears out any data cached from the previous CommandMap. 1.444 + * 1.445 + * @param commandMap the CommandMap to use in this DataHandler 1.446 + * 1.447 + * @see javax.activation.CommandMap#setDefaultCommandMap 1.448 + */ 1.449 + public synchronized void setCommandMap(CommandMap commandMap) { 1.450 + if (commandMap != currentCommandMap || commandMap == null) { 1.451 + // clear cached values... 1.452 + transferFlavors = emptyFlavors; 1.453 + dataContentHandler = null; 1.454 + 1.455 + currentCommandMap = commandMap; 1.456 + } 1.457 + } 1.458 + 1.459 + /** 1.460 + * Return the <i>preferred</i> commands for this type of data. 1.461 + * This method calls the <code>getPreferredCommands</code> method 1.462 + * in the CommandMap associated with this instance of DataHandler. 1.463 + * This method returns an array that represents a subset of 1.464 + * available commands. In cases where multiple commands for the 1.465 + * MIME type represented by this DataHandler are present, the 1.466 + * installed CommandMap chooses the appropriate commands. 1.467 + * 1.468 + * @return the CommandInfo objects representing the preferred commands 1.469 + * 1.470 + * @see javax.activation.CommandMap#getPreferredCommands 1.471 + */ 1.472 + public CommandInfo[] getPreferredCommands() { 1.473 + if (dataSource != null) 1.474 + return getCommandMap().getPreferredCommands(getBaseType(), 1.475 + dataSource); 1.476 + else 1.477 + return getCommandMap().getPreferredCommands(getBaseType()); 1.478 + } 1.479 + 1.480 + /** 1.481 + * Return all the commands for this type of data. 1.482 + * This method returns an array containing all commands 1.483 + * for the type of data represented by this DataHandler. The 1.484 + * MIME type for the underlying data represented by this DataHandler 1.485 + * is used to call through to the <code>getAllCommands</code> method 1.486 + * of the CommandMap associated with this DataHandler. 1.487 + * 1.488 + * @return the CommandInfo objects representing all the commands 1.489 + * 1.490 + * @see javax.activation.CommandMap#getAllCommands 1.491 + */ 1.492 + public CommandInfo[] getAllCommands() { 1.493 + if (dataSource != null) 1.494 + return getCommandMap().getAllCommands(getBaseType(), dataSource); 1.495 + else 1.496 + return getCommandMap().getAllCommands(getBaseType()); 1.497 + } 1.498 + 1.499 + /** 1.500 + * Get the command <i>cmdName</i>. Use the search semantics as 1.501 + * defined by the CommandMap installed in this DataHandler. The 1.502 + * MIME type for the underlying data represented by this DataHandler 1.503 + * is used to call through to the <code>getCommand</code> method 1.504 + * of the CommandMap associated with this DataHandler. 1.505 + * 1.506 + * @param cmdName the command name 1.507 + * @return the CommandInfo corresponding to the command 1.508 + * 1.509 + * @see javax.activation.CommandMap#getCommand 1.510 + */ 1.511 + public CommandInfo getCommand(String cmdName) { 1.512 + if (dataSource != null) 1.513 + return getCommandMap().getCommand(getBaseType(), cmdName, 1.514 + dataSource); 1.515 + else 1.516 + return getCommandMap().getCommand(getBaseType(), cmdName); 1.517 + } 1.518 + 1.519 + /** 1.520 + * Return the data in its preferred Object form. <p> 1.521 + * 1.522 + * If the DataHandler was instantiated with an object, return 1.523 + * the object. <p> 1.524 + * 1.525 + * If the DataHandler was instantiated with a DataSource, 1.526 + * this method uses a DataContentHandler to return the content 1.527 + * object for the data represented by this DataHandler. If no 1.528 + * <code>DataContentHandler</code> can be found for the 1.529 + * the type of this data, the DataHandler returns an 1.530 + * InputStream for the data. 1.531 + * 1.532 + * @return the content. 1.533 + * @exception IOException if an IOException occurs during 1.534 + * this operation. 1.535 + */ 1.536 + public Object getContent() throws IOException { 1.537 + if (object != null) 1.538 + return object; 1.539 + else 1.540 + return getDataContentHandler().getContent(getDataSource()); 1.541 + } 1.542 + 1.543 + /** 1.544 + * A convenience method that takes a CommandInfo object 1.545 + * and instantiates the corresponding command, usually 1.546 + * a JavaBean component. 1.547 + * <p> 1.548 + * This method calls the CommandInfo's <code>getCommandObject</code> 1.549 + * method with the <code>ClassLoader</code> used to load 1.550 + * the <code>javax.activation.DataHandler</code> class itself. 1.551 + * 1.552 + * @param cmdinfo the CommandInfo corresponding to a command 1.553 + * @return the instantiated command object 1.554 + */ 1.555 + public Object getBean(CommandInfo cmdinfo) { 1.556 + Object bean = null; 1.557 + 1.558 + try { 1.559 + // make the bean 1.560 + ClassLoader cld = null; 1.561 + // First try the "application's" class loader. 1.562 + cld = SecuritySupport.getContextClassLoader(); 1.563 + if (cld == null) 1.564 + cld = this.getClass().getClassLoader(); 1.565 + bean = cmdinfo.getCommandObject(this, cld); 1.566 + } catch (IOException e) { 1.567 + } catch (ClassNotFoundException e) { } 1.568 + 1.569 + return bean; 1.570 + } 1.571 + 1.572 + /** 1.573 + * Get the DataContentHandler for this DataHandler: <p> 1.574 + * 1.575 + * If a DataContentHandlerFactory is set, use it. 1.576 + * Otherwise look for an object to serve DCH in the 1.577 + * following order: <p> 1.578 + * 1.579 + * 1) if a factory is set, use it <p> 1.580 + * 2) if a CommandMap is set, use it <p> 1.581 + * 3) use the default CommandMap <p> 1.582 + * 1.583 + * In any case, wrap the real DataContentHandler with one of our own 1.584 + * to handle any missing cases, fill in defaults, and to ensure that 1.585 + * we always have a non-null DataContentHandler. 1.586 + * 1.587 + * @return the requested DataContentHandler 1.588 + */ 1.589 + private synchronized DataContentHandler getDataContentHandler() { 1.590 + 1.591 + // make sure the factory didn't change 1.592 + if (factory != oldFactory) { 1.593 + oldFactory = factory; 1.594 + factoryDCH = null; 1.595 + dataContentHandler = null; 1.596 + transferFlavors = emptyFlavors; 1.597 + } 1.598 + 1.599 + if (dataContentHandler != null) 1.600 + return dataContentHandler; 1.601 + 1.602 + String simpleMT = getBaseType(); 1.603 + 1.604 + if (factoryDCH == null && factory != null) 1.605 + factoryDCH = factory.createDataContentHandler(simpleMT); 1.606 + 1.607 + if (factoryDCH != null) 1.608 + dataContentHandler = factoryDCH; 1.609 + 1.610 + if (dataContentHandler == null) { 1.611 + if (dataSource != null) 1.612 + dataContentHandler = getCommandMap(). 1.613 + createDataContentHandler(simpleMT, dataSource); 1.614 + else 1.615 + dataContentHandler = getCommandMap(). 1.616 + createDataContentHandler(simpleMT); 1.617 + } 1.618 + 1.619 + // getDataContentHandler always uses these 'wrapper' handlers 1.620 + // to make sure it returns SOMETHING meaningful... 1.621 + if (dataSource != null) 1.622 + dataContentHandler = new DataSourceDataContentHandler( 1.623 + dataContentHandler, 1.624 + dataSource); 1.625 + else 1.626 + dataContentHandler = new ObjectDataContentHandler( 1.627 + dataContentHandler, 1.628 + object, 1.629 + objectMimeType); 1.630 + return dataContentHandler; 1.631 + } 1.632 + 1.633 + /** 1.634 + * Use the MimeType class to extract the MIME type/subtype, 1.635 + * ignoring the parameters. The type is cached. 1.636 + */ 1.637 + private synchronized String getBaseType() { 1.638 + if (shortType == null) { 1.639 + String ct = getContentType(); 1.640 + try { 1.641 + MimeType mt = new MimeType(ct); 1.642 + shortType = mt.getBaseType(); 1.643 + } catch (MimeTypeParseException e) { 1.644 + shortType = ct; 1.645 + } 1.646 + } 1.647 + return shortType; 1.648 + } 1.649 + 1.650 + /** 1.651 + * Sets the DataContentHandlerFactory. The DataContentHandlerFactory 1.652 + * is called first to find DataContentHandlers. 1.653 + * The DataContentHandlerFactory can only be set once. 1.654 + * <p> 1.655 + * If the DataContentHandlerFactory has already been set, 1.656 + * this method throws an Error. 1.657 + * 1.658 + * @param newFactory the DataContentHandlerFactory 1.659 + * @exception Error if the factory has already been defined. 1.660 + * 1.661 + * @see javax.activation.DataContentHandlerFactory 1.662 + */ 1.663 + public static synchronized void setDataContentHandlerFactory( 1.664 + DataContentHandlerFactory newFactory) { 1.665 + if (factory != null) 1.666 + throw new Error("DataContentHandlerFactory already defined"); 1.667 + 1.668 + SecurityManager security = System.getSecurityManager(); 1.669 + if (security != null) { 1.670 + try { 1.671 + // if it's ok with the SecurityManager, it's ok with me... 1.672 + security.checkSetFactory(); 1.673 + } catch (SecurityException ex) { 1.674 + // otherwise, we also allow it if this code and the 1.675 + // factory come from the same class loader (e.g., 1.676 + // the JAF classes were loaded with the applet classes). 1.677 + if (DataHandler.class.getClassLoader() != 1.678 + newFactory.getClass().getClassLoader()) 1.679 + throw ex; 1.680 + } 1.681 + } 1.682 + factory = newFactory; 1.683 + } 1.684 +} 1.685 + 1.686 +/** 1.687 + * The DataHanderDataSource class implements the 1.688 + * DataSource interface when the DataHandler is constructed 1.689 + * with an Object and a mimeType string. 1.690 + */ 1.691 +class DataHandlerDataSource implements DataSource { 1.692 + DataHandler dataHandler = null; 1.693 + 1.694 + /** 1.695 + * The constructor. 1.696 + */ 1.697 + public DataHandlerDataSource(DataHandler dh) { 1.698 + this.dataHandler = dh; 1.699 + } 1.700 + 1.701 + /** 1.702 + * Returns an <code>InputStream</code> representing this object. 1.703 + * @return the <code>InputStream</code> 1.704 + */ 1.705 + public InputStream getInputStream() throws IOException { 1.706 + return dataHandler.getInputStream(); 1.707 + } 1.708 + 1.709 + /** 1.710 + * Returns the <code>OutputStream</code> for this object. 1.711 + * @return the <code>OutputStream</code> 1.712 + */ 1.713 + public OutputStream getOutputStream() throws IOException { 1.714 + return dataHandler.getOutputStream(); 1.715 + } 1.716 + 1.717 + /** 1.718 + * Returns the MIME type of the data represented by this object. 1.719 + * @return the MIME type 1.720 + */ 1.721 + public String getContentType() { 1.722 + return dataHandler.getContentType(); 1.723 + } 1.724 + 1.725 + /** 1.726 + * Returns the name of this object. 1.727 + * @return the name of this object 1.728 + */ 1.729 + public String getName() { 1.730 + return dataHandler.getName(); // what else would it be? 1.731 + } 1.732 +} 1.733 + 1.734 +/* 1.735 + * DataSourceDataContentHandler 1.736 + * 1.737 + * This is a <i>private</i> DataContentHandler that wraps the real 1.738 + * DataContentHandler in the case where the DataHandler was instantiated 1.739 + * with a DataSource. 1.740 + */ 1.741 +class DataSourceDataContentHandler implements DataContentHandler { 1.742 + private DataSource ds = null; 1.743 + private DataFlavor transferFlavors[] = null; 1.744 + private DataContentHandler dch = null; 1.745 + 1.746 + /** 1.747 + * The constructor. 1.748 + */ 1.749 + public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { 1.750 + this.ds = ds; 1.751 + this.dch = dch; 1.752 + } 1.753 + 1.754 + /** 1.755 + * Return the DataFlavors for this <code>DataContentHandler</code>. 1.756 + * @return the DataFlavors 1.757 + */ 1.758 + public DataFlavor[] getTransferDataFlavors() { 1.759 + 1.760 + if (transferFlavors == null) { 1.761 + if (dch != null) { // is there a dch? 1.762 + transferFlavors = dch.getTransferDataFlavors(); 1.763 + } else { 1.764 + transferFlavors = new DataFlavor[1]; 1.765 + transferFlavors[0] = 1.766 + new ActivationDataFlavor(ds.getContentType(), 1.767 + ds.getContentType()); 1.768 + } 1.769 + } 1.770 + return transferFlavors; 1.771 + } 1.772 + 1.773 + /** 1.774 + * Return the Transfer Data of type DataFlavor from InputStream. 1.775 + * @param df the DataFlavor 1.776 + * @param ds the DataSource 1.777 + * @return the constructed Object 1.778 + */ 1.779 + public Object getTransferData(DataFlavor df, DataSource ds) throws 1.780 + UnsupportedFlavorException, IOException { 1.781 + 1.782 + if (dch != null) 1.783 + return dch.getTransferData(df, ds); 1.784 + else if (df.equals(getTransferDataFlavors()[0])) // only have one now 1.785 + return ds.getInputStream(); 1.786 + else 1.787 + throw new UnsupportedFlavorException(df); 1.788 + } 1.789 + 1.790 + public Object getContent(DataSource ds) throws IOException { 1.791 + 1.792 + if (dch != null) 1.793 + return dch.getContent(ds); 1.794 + else 1.795 + return ds.getInputStream(); 1.796 + } 1.797 + 1.798 + /** 1.799 + * Write the object to the output stream. 1.800 + */ 1.801 + public void writeTo(Object obj, String mimeType, OutputStream os) 1.802 + throws IOException { 1.803 + if (dch != null) 1.804 + dch.writeTo(obj, mimeType, os); 1.805 + else 1.806 + throw new UnsupportedDataTypeException( 1.807 + "no DCH for content type " + ds.getContentType()); 1.808 + } 1.809 +} 1.810 + 1.811 +/* 1.812 + * ObjectDataContentHandler 1.813 + * 1.814 + * This is a <i>private</i> DataContentHandler that wraps the real 1.815 + * DataContentHandler in the case where the DataHandler was instantiated 1.816 + * with an object. 1.817 + */ 1.818 +class ObjectDataContentHandler implements DataContentHandler { 1.819 + private DataFlavor transferFlavors[] = null; 1.820 + private Object obj; 1.821 + private String mimeType; 1.822 + private DataContentHandler dch = null; 1.823 + 1.824 + /** 1.825 + * The constructor. 1.826 + */ 1.827 + public ObjectDataContentHandler(DataContentHandler dch, 1.828 + Object obj, String mimeType) { 1.829 + this.obj = obj; 1.830 + this.mimeType = mimeType; 1.831 + this.dch = dch; 1.832 + } 1.833 + 1.834 + /** 1.835 + * Return the DataContentHandler for this object. 1.836 + * Used only by the DataHandler class. 1.837 + */ 1.838 + public DataContentHandler getDCH() { 1.839 + return dch; 1.840 + } 1.841 + 1.842 + /** 1.843 + * Return the DataFlavors for this <code>DataContentHandler</code>. 1.844 + * @return the DataFlavors 1.845 + */ 1.846 + public synchronized DataFlavor[] getTransferDataFlavors() { 1.847 + if (transferFlavors == null) { 1.848 + if (dch != null) { 1.849 + transferFlavors = dch.getTransferDataFlavors(); 1.850 + } else { 1.851 + transferFlavors = new DataFlavor[1]; 1.852 + transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), 1.853 + mimeType, mimeType); 1.854 + } 1.855 + } 1.856 + return transferFlavors; 1.857 + } 1.858 + 1.859 + /** 1.860 + * Return the Transfer Data of type DataFlavor from InputStream. 1.861 + * @param df the DataFlavor 1.862 + * @param ds the DataSource 1.863 + * @return the constructed Object 1.864 + */ 1.865 + public Object getTransferData(DataFlavor df, DataSource ds) 1.866 + throws UnsupportedFlavorException, IOException { 1.867 + 1.868 + if (dch != null) 1.869 + return dch.getTransferData(df, ds); 1.870 + else if (df.equals(getTransferDataFlavors()[0])) // only have one now 1.871 + return obj; 1.872 + else 1.873 + throw new UnsupportedFlavorException(df); 1.874 + 1.875 + } 1.876 + 1.877 + public Object getContent(DataSource ds) { 1.878 + return obj; 1.879 + } 1.880 + 1.881 + /** 1.882 + * Write the object to the output stream. 1.883 + */ 1.884 + public void writeTo(Object obj, String mimeType, OutputStream os) 1.885 + throws IOException { 1.886 + if (dch != null) 1.887 + dch.writeTo(obj, mimeType, os); 1.888 + else if (obj instanceof byte[]) 1.889 + os.write((byte[])obj); 1.890 + else if (obj instanceof String) { 1.891 + OutputStreamWriter osw = new OutputStreamWriter(os); 1.892 + osw.write((String)obj); 1.893 + osw.flush(); 1.894 + } else throw new UnsupportedDataTypeException( 1.895 + "no object DCH for MIME type " + this.mimeType); 1.896 + } 1.897 +}