1 /* 2 * Copyright (c) 1997, 2012, 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 26 package javax.activation; 27 28 import java.io.InputStream; 29 import java.io.IOException; 30 import java.io.OutputStream; 31 import java.io.PipedInputStream; 32 import java.io.PipedOutputStream; 33 import java.io.OutputStreamWriter; 34 import java.net.URL; 35 import java.awt.datatransfer.Transferable; 36 import java.awt.datatransfer.DataFlavor; 37 import java.awt.datatransfer.UnsupportedFlavorException; 38 39 /** 40 * The DataHandler class provides a consistent interface to data 41 * available in many different sources and formats. 42 * It manages simple stream to string conversions and related operations 43 * using DataContentHandlers. 44 * It provides access to commands that can operate on the data. 45 * The commands are found using a CommandMap. <p> 46 * 47 * <b>DataHandler and the Transferable Interface</b><p> 48 * DataHandler implements the Transferable interface so that data can 49 * be used in AWT data transfer operations, such as cut and paste and 50 * drag and drop. The implementation of the Transferable interface 51 * relies on the availability of an installed DataContentHandler 52 * object corresponding to the MIME type of the data represented in 53 * the specific instance of the DataHandler.<p> 54 * 55 * <b>DataHandler and CommandMaps</b><p> 56 * The DataHandler keeps track of the current CommandMap that it uses to 57 * service requests for commands ({@code getCommand, getAllCommands, 58 * getPreferredCommands}). 59 * Each instance of a DataHandler may have a CommandMap associated with 60 * it using the {@code setCommandMap} method. If a CommandMap was 61 * not set, DataHandler calls the {@code getDefaultCommandMap} 62 * method in CommandMap and uses the value it returns. See 63 * <i>CommandMap</i> for more information. <p> 64 * 65 * <b>DataHandler and URLs</b><p> 66 * The current DataHandler implementation creates a private 67 * instance of URLDataSource when it is constructed with a URL. 68 * 69 * @see javax.activation.CommandMap 70 * @see javax.activation.DataContentHandler 71 * @see javax.activation.DataSource 72 * @see javax.activation.URLDataSource 73 * 74 * @since 1.6 75 */ 76 77 public class DataHandler implements Transferable { 78 79 // Use the datasource to indicate whether we were started via the 80 // DataSource constructor or the object constructor. 81 private DataSource dataSource = null; 82 private DataSource objDataSource = null; 83 84 // The Object and mimetype from the constructor (if passed in). 85 // object remains null if it was instantiated with a 86 // DataSource. 87 private Object object = null; 88 private String objectMimeType = null; 89 90 // Keep track of the CommandMap 91 private CommandMap currentCommandMap = null; 92 93 // our transfer flavors 94 private static final DataFlavor emptyFlavors[] = new DataFlavor[0]; 95 private DataFlavor transferFlavors[] = emptyFlavors; 96 97 // our DataContentHandler 98 private DataContentHandler dataContentHandler = null; 99 private DataContentHandler factoryDCH = null; 100 101 // our DataContentHandlerFactory 102 private static DataContentHandlerFactory factory = null; 103 private DataContentHandlerFactory oldFactory = null; 104 // the short representation of the ContentType (sans params) 105 private String shortType = null; 106 107 /** 108 * Create a {@code DataHandler} instance referencing the 109 * specified DataSource. The data exists in a byte stream form. 110 * The DataSource will provide an InputStream to access the data. 111 * 112 * @param ds the DataSource 113 */ 114 public DataHandler(DataSource ds) { 115 // save a reference to the incoming DS 116 dataSource = ds; 117 oldFactory = factory; // keep track of the factory 118 } 119 120 /** 121 * Create a {@code DataHandler} instance representing an object 122 * of this MIME type. This constructor is 123 * used when the application already has an in-memory representation 124 * of the data in the form of a Java Object. 125 * 126 * @param obj the Java Object 127 * @param mimeType the MIME type of the object 128 */ 129 public DataHandler(Object obj, String mimeType) { 130 object = obj; 131 objectMimeType = mimeType; 132 oldFactory = factory; // keep track of the factory 133 } 134 135 /** 136 * Create a {@code DataHandler} instance referencing a URL. 137 * The DataHandler internally creates a {@code URLDataSource} 138 * instance to represent the URL. 139 * 140 * @param url a URL object 141 */ 142 public DataHandler(URL url) { 143 dataSource = new URLDataSource(url); 144 oldFactory = factory; // keep track of the factory 145 } 146 147 /** 148 * Return the CommandMap for this instance of DataHandler. 149 */ 150 private synchronized CommandMap getCommandMap() { 151 if (currentCommandMap != null) 152 return currentCommandMap; 153 else 154 return CommandMap.getDefaultCommandMap(); 155 } 156 157 /** 158 * Return the DataSource associated with this instance 159 * of DataHandler. 160 * <p> 161 * For DataHandlers that have been instantiated with a DataSource, 162 * this method returns the DataSource that was used to create the 163 * DataHandler object. In other cases the DataHandler 164 * constructs a DataSource from the data used to construct 165 * the DataHandler. DataSources created for DataHandlers <b>not</b> 166 * instantiated with a DataSource are cached for performance 167 * reasons. 168 * 169 * @return a valid DataSource object for this DataHandler 170 */ 171 public DataSource getDataSource() { 172 if (dataSource == null) { 173 // create one on the fly 174 if (objDataSource == null) 175 objDataSource = new DataHandlerDataSource(this); 176 return objDataSource; 177 } 178 return dataSource; 179 } 180 181 /** 182 * Return the name of the data object. If this DataHandler 183 * was created with a DataSource, this method calls through 184 * to the {@code DataSource.getName} method, otherwise it 185 * returns <i>null</i>. 186 * 187 * @return the name of the object 188 */ 189 public String getName() { 190 if (dataSource != null) 191 return dataSource.getName(); 192 else 193 return null; 194 } 195 196 /** 197 * Return the MIME type of this object as retrieved from 198 * the source object. Note that this is the <i>full</i> 199 * type with parameters. 200 * 201 * @return the MIME type 202 */ 203 public String getContentType() { 204 if (dataSource != null) // data source case 205 return dataSource.getContentType(); 206 else 207 return objectMimeType; // obj/type case 208 } 209 210 /** 211 * Get the InputStream for this object. <p> 212 * 213 * For DataHandlers instantiated with a DataSource, the DataHandler 214 * calls the {@code DataSource.getInputStream} method and 215 * returns the result to the caller. 216 * <p> 217 * For DataHandlers instantiated with an Object, the DataHandler 218 * first attempts to find a DataContentHandler for the Object. If 219 * the DataHandler can not find a DataContentHandler for this MIME 220 * type, it throws an UnsupportedDataTypeException. If it is 221 * successful, it creates a pipe and a thread. The thread uses the 222 * DataContentHandler's {@code writeTo} method to write the 223 * stream data into one end of the pipe. The other end of the pipe 224 * is returned to the caller. Because a thread is created to copy 225 * the data, IOExceptions that may occur during the copy can not be 226 * propagated back to the caller. The result is an empty stream. 227 * 228 * @return the InputStream representing this data 229 * @exception IOException if an I/O error occurs 230 * 231 * @see javax.activation.DataContentHandler#writeTo 232 * @see javax.activation.UnsupportedDataTypeException 233 */ 234 public InputStream getInputStream() throws IOException { 235 InputStream ins = null; 236 237 if (dataSource != null) { 238 ins = dataSource.getInputStream(); 239 } else { 240 DataContentHandler dch = getDataContentHandler(); 241 // we won't even try if we can't get a dch 242 if (dch == null) 243 throw new UnsupportedDataTypeException( 244 "no DCH for MIME type " + getBaseType()); 245 246 if (dch instanceof ObjectDataContentHandler) { 247 if (((ObjectDataContentHandler)dch).getDCH() == null) 248 throw new UnsupportedDataTypeException( 249 "no object DCH for MIME type " + getBaseType()); 250 } 251 // there is none but the default^^^^^^^^^^^^^^^^ 252 final DataContentHandler fdch = dch; 253 254 // from bill s. 255 // ce n'est pas une pipe! 256 // 257 // NOTE: This block of code needs to throw exceptions, but 258 // can't because it is in another thread!!! ARG! 259 // 260 final PipedOutputStream pos = new PipedOutputStream(); 261 PipedInputStream pin = new PipedInputStream(pos); 262 new Thread( 263 new Runnable() { 264 public void run() { 265 try { 266 fdch.writeTo(object, objectMimeType, pos); 267 } catch (IOException e) { 268 269 } finally { 270 try { 271 pos.close(); 272 } catch (IOException ie) { } 273 } 274 } 275 }, 276 "DataHandler.getInputStream").start(); 277 ins = pin; 278 } 279 280 return ins; 281 } 282 283 /** 284 * Write the data to an {@code OutputStream}.<p> 285 * 286 * If the DataHandler was created with a DataSource, writeTo 287 * retrieves the InputStream and copies the bytes from the 288 * InputStream to the OutputStream passed in. 289 * <p> 290 * If the DataHandler was created with an object, writeTo 291 * retrieves the DataContentHandler for the object's type. 292 * If the DataContentHandler was found, it calls the 293 * {@code writeTo} method on the {@code DataContentHandler}. 294 * 295 * @param os the OutputStream to write to 296 * @exception IOException if an I/O error occurs 297 */ 298 public void writeTo(OutputStream os) throws IOException { 299 // for the DataSource case 300 if (dataSource != null) { 301 InputStream is = null; 302 byte data[] = new byte[8*1024]; 303 int bytes_read; 304 305 is = dataSource.getInputStream(); 306 307 try { 308 while ((bytes_read = is.read(data)) > 0) { 309 os.write(data, 0, bytes_read); 310 } 311 } finally { 312 is.close(); 313 is = null; 314 } 315 } else { // for the Object case 316 DataContentHandler dch = getDataContentHandler(); 317 dch.writeTo(object, objectMimeType, os); 318 } 319 } 320 321 /** 322 * Get an OutputStream for this DataHandler to allow overwriting 323 * the underlying data. 324 * If the DataHandler was created with a DataSource, the 325 * DataSource's {@code getOutputStream} method is called. 326 * Otherwise, {@code null} is returned. 327 * 328 * @return the OutputStream 329 * 330 * @see javax.activation.DataSource#getOutputStream 331 * @see javax.activation.URLDataSource 332 */ 333 public OutputStream getOutputStream() throws IOException { 334 if (dataSource != null) 335 return dataSource.getOutputStream(); 336 else 337 return null; 338 } 339 340 /** 341 * Return the DataFlavors in which this data is available. <p> 342 * 343 * Returns an array of DataFlavor objects indicating the flavors 344 * the data can be provided in. The array is usually ordered 345 * according to preference for providing the data, from most 346 * richly descriptive to least richly descriptive.<p> 347 * 348 * The DataHandler attempts to find a DataContentHandler that 349 * corresponds to the MIME type of the data. If one is located, 350 * the DataHandler calls the DataContentHandler's 351 * {@code getTransferDataFlavors} method. <p> 352 * 353 * If a DataContentHandler can <i>not</i> be located, and if the 354 * DataHandler was created with a DataSource (or URL), one 355 * DataFlavor is returned that represents this object's MIME type 356 * and the {@code java.io.InputStream} class. If the 357 * DataHandler was created with an object and a MIME type, 358 * getTransferDataFlavors returns one DataFlavor that represents 359 * this object's MIME type and the object's class. 360 * 361 * @return an array of data flavors in which this data can be transferred 362 * @see javax.activation.DataContentHandler#getTransferDataFlavors 363 */ 364 public synchronized DataFlavor[] getTransferDataFlavors() { 365 if (factory != oldFactory) // if the factory has changed, clear cache 366 transferFlavors = emptyFlavors; 367 368 // if it's not set, set it... 369 if (transferFlavors == emptyFlavors) 370 transferFlavors = getDataContentHandler().getTransferDataFlavors(); 371 372 if (transferFlavors == emptyFlavors) 373 return transferFlavors; 374 else 375 return transferFlavors.clone(); 376 377 } 378 379 /** 380 * Returns whether the specified data flavor is supported 381 * for this object.<p> 382 * 383 * This method iterates through the DataFlavors returned from 384 * {@code getTransferDataFlavors}, comparing each with 385 * the specified flavor. 386 * 387 * @param flavor the requested flavor for the data 388 * @return true if the data flavor is supported 389 * @see javax.activation.DataHandler#getTransferDataFlavors 390 */ 391 public boolean isDataFlavorSupported(DataFlavor flavor) { 392 DataFlavor[] lFlavors = getTransferDataFlavors(); 393 394 for (int i = 0; i < lFlavors.length; i++) { 395 if (lFlavors[i].equals(flavor)) 396 return true; 397 } 398 return false; 399 } 400 401 /** 402 * Returns an object that represents the data to be 403 * transferred. The class of the object returned is defined by the 404 * representation class of the data flavor.<p> 405 * 406 * <b>For DataHandler's created with DataSources or URLs:</b><p> 407 * 408 * The DataHandler attempts to locate a DataContentHandler 409 * for this MIME type. If one is found, the passed in DataFlavor 410 * and the type of the data are passed to its {@code getTransferData} 411 * method. If the DataHandler fails to locate a DataContentHandler 412 * and the flavor specifies this object's MIME type and the 413 * {@code java.io.InputStream} class, this object's InputStream 414 * is returned. 415 * Otherwise it throws an UnsupportedFlavorException. <p> 416 * 417 * <b>For DataHandler's created with Objects:</b><p> 418 * 419 * The DataHandler attempts to locate a DataContentHandler 420 * for this MIME type. If one is found, the passed in DataFlavor 421 * and the type of the data are passed to its getTransferData 422 * method. If the DataHandler fails to locate a DataContentHandler 423 * and the flavor specifies this object's MIME type and its class, 424 * this DataHandler's referenced object is returned. 425 * Otherwise it throws an UnsupportedFlavorException. 426 * 427 * @param flavor the requested flavor for the data 428 * @return the object 429 * @exception UnsupportedFlavorException if the data could not be 430 * converted to the requested flavor 431 * @exception IOException if an I/O error occurs 432 * @see javax.activation.ActivationDataFlavor 433 */ 434 public Object getTransferData(DataFlavor flavor) 435 throws UnsupportedFlavorException, IOException { 436 return getDataContentHandler().getTransferData(flavor, dataSource); 437 } 438 439 /** 440 * Set the CommandMap for use by this DataHandler. 441 * Setting it to {@code null} causes the CommandMap to revert 442 * to the CommandMap returned by the 443 * {@code CommandMap.getDefaultCommandMap} method. 444 * Changing the CommandMap, or setting it to {@code null}, 445 * clears out any data cached from the previous CommandMap. 446 * 447 * @param commandMap the CommandMap to use in this DataHandler 448 * 449 * @see javax.activation.CommandMap#setDefaultCommandMap 450 */ 451 public synchronized void setCommandMap(CommandMap commandMap) { 452 if (commandMap != currentCommandMap || commandMap == null) { 453 // clear cached values... 454 transferFlavors = emptyFlavors; 455 dataContentHandler = null; 456 457 currentCommandMap = commandMap; 458 } 459 } 460 461 /** 462 * Return the <i>preferred</i> commands for this type of data. 463 * This method calls the {@code getPreferredCommands} method 464 * in the CommandMap associated with this instance of DataHandler. 465 * This method returns an array that represents a subset of 466 * available commands. In cases where multiple commands for the 467 * MIME type represented by this DataHandler are present, the 468 * installed CommandMap chooses the appropriate commands. 469 * 470 * @return the CommandInfo objects representing the preferred commands 471 * 472 * @see javax.activation.CommandMap#getPreferredCommands 473 */ 474 public CommandInfo[] getPreferredCommands() { 475 if (dataSource != null) 476 return getCommandMap().getPreferredCommands(getBaseType(), 477 dataSource); 478 else 479 return getCommandMap().getPreferredCommands(getBaseType()); 480 } 481 482 /** 483 * Return all the commands for this type of data. 484 * This method returns an array containing all commands 485 * for the type of data represented by this DataHandler. The 486 * MIME type for the underlying data represented by this DataHandler 487 * is used to call through to the {@code getAllCommands} method 488 * of the CommandMap associated with this DataHandler. 489 * 490 * @return the CommandInfo objects representing all the commands 491 * 492 * @see javax.activation.CommandMap#getAllCommands 493 */ 494 public CommandInfo[] getAllCommands() { 495 if (dataSource != null) 496 return getCommandMap().getAllCommands(getBaseType(), dataSource); 497 else 498 return getCommandMap().getAllCommands(getBaseType()); 499 } 500 501 /** 502 * Get the command <i>cmdName</i>. Use the search semantics as 503 * defined by the CommandMap installed in this DataHandler. The 504 * MIME type for the underlying data represented by this DataHandler 505 * is used to call through to the {@code getCommand} method 506 * of the CommandMap associated with this DataHandler. 507 * 508 * @param cmdName the command name 509 * @return the CommandInfo corresponding to the command 510 * 511 * @see javax.activation.CommandMap#getCommand 512 */ 513 public CommandInfo getCommand(String cmdName) { 514 if (dataSource != null) 515 return getCommandMap().getCommand(getBaseType(), cmdName, 516 dataSource); 517 else 518 return getCommandMap().getCommand(getBaseType(), cmdName); 519 } 520 521 /** 522 * Return the data in its preferred Object form. <p> 523 * 524 * If the DataHandler was instantiated with an object, return 525 * the object. <p> 526 * 527 * If the DataHandler was instantiated with a DataSource, 528 * this method uses a DataContentHandler to return the content 529 * object for the data represented by this DataHandler. If no 530 * {@code DataContentHandler} can be found for the 531 * the type of this data, the DataHandler returns an 532 * InputStream for the data. 533 * 534 * @return the content. 535 * @exception IOException if an IOException occurs during 536 * this operation. 537 */ 538 public Object getContent() throws IOException { 539 if (object != null) 540 return object; 541 else 542 return getDataContentHandler().getContent(getDataSource()); 543 } 544 545 /** 546 * A convenience method that takes a CommandInfo object 547 * and instantiates the corresponding command, usually 548 * a JavaBean component. 549 * <p> 550 * This method calls the CommandInfo's {@code getCommandObject} 551 * method with the {@code ClassLoader} used to load 552 * the {@code javax.activation.DataHandler} class itself. 553 * 554 * @param cmdinfo the CommandInfo corresponding to a command 555 * @return the instantiated command object 556 */ 557 public Object getBean(CommandInfo cmdinfo) { 558 Object bean = null; 559 560 try { 561 // make the bean 562 ClassLoader cld = null; 563 // First try the "application's" class loader. 564 cld = SecuritySupport.getContextClassLoader(); 565 if (cld == null) 566 cld = this.getClass().getClassLoader(); 567 bean = cmdinfo.getCommandObject(this, cld); 568 } catch (IOException e) { 569 } catch (ClassNotFoundException e) { } 570 571 return bean; 572 } 573 574 /** 575 * Get the DataContentHandler for this DataHandler: <p> 576 * 577 * If a DataContentHandlerFactory is set, use it. 578 * Otherwise look for an object to serve DCH in the 579 * following order: <p> 580 * 581 * 1) if a factory is set, use it <p> 582 * 2) if a CommandMap is set, use it <p> 583 * 3) use the default CommandMap <p> 584 * 585 * In any case, wrap the real DataContentHandler with one of our own 586 * to handle any missing cases, fill in defaults, and to ensure that 587 * we always have a non-null DataContentHandler. 588 * 589 * @return the requested DataContentHandler 590 */ 591 private synchronized DataContentHandler getDataContentHandler() { 592 593 // make sure the factory didn't change 594 if (factory != oldFactory) { 595 oldFactory = factory; 596 factoryDCH = null; 597 dataContentHandler = null; 598 transferFlavors = emptyFlavors; 599 } 600 601 if (dataContentHandler != null) 602 return dataContentHandler; 603 604 String simpleMT = getBaseType(); 605 606 if (factoryDCH == null && factory != null) 607 factoryDCH = factory.createDataContentHandler(simpleMT); 608 609 if (factoryDCH != null) 610 dataContentHandler = factoryDCH; 611 612 if (dataContentHandler == null) { 613 if (dataSource != null) 614 dataContentHandler = getCommandMap(). 615 createDataContentHandler(simpleMT, dataSource); 616 else 617 dataContentHandler = getCommandMap(). 618 createDataContentHandler(simpleMT); 619 } 620 621 // getDataContentHandler always uses these 'wrapper' handlers 622 // to make sure it returns SOMETHING meaningful... 623 if (dataSource != null) 624 dataContentHandler = new DataSourceDataContentHandler( 625 dataContentHandler, 626 dataSource); 627 else 628 dataContentHandler = new ObjectDataContentHandler( 629 dataContentHandler, 630 object, 631 objectMimeType); 632 return dataContentHandler; 633 } 634 635 /** 636 * Use the MimeType class to extract the MIME type/subtype, 637 * ignoring the parameters. The type is cached. 638 */ 639 private synchronized String getBaseType() { 640 if (shortType == null) { 641 String ct = getContentType(); 642 try { 643 MimeType mt = new MimeType(ct); 644 shortType = mt.getBaseType(); 645 } catch (MimeTypeParseException e) { 646 shortType = ct; 647 } 648 } 649 return shortType; 650 } 651 652 /** 653 * Sets the DataContentHandlerFactory. The DataContentHandlerFactory 654 * is called first to find DataContentHandlers. 655 * The DataContentHandlerFactory can only be set once. 656 * <p> 657 * If the DataContentHandlerFactory has already been set, 658 * this method throws an Error. 659 * 660 * @param newFactory the DataContentHandlerFactory 661 * @exception Error if the factory has already been defined. 662 * 663 * @see javax.activation.DataContentHandlerFactory 664 */ 665 public static synchronized void setDataContentHandlerFactory( 666 DataContentHandlerFactory newFactory) { 667 if (factory != null) 668 throw new Error("DataContentHandlerFactory already defined"); 669 670 SecurityManager security = System.getSecurityManager(); 671 if (security != null) { 672 try { 673 // if it's ok with the SecurityManager, it's ok with me... 674 security.checkSetFactory(); 675 } catch (SecurityException ex) { 676 // otherwise, we also allow it if this code and the 677 // factory come from the same class loader (e.g., 678 // the JAF classes were loaded with the applet classes). 679 if (DataHandler.class.getClassLoader() != 680 newFactory.getClass().getClassLoader()) 681 throw ex; 682 } 683 } 684 factory = newFactory; 685 } 686 } 687 688 /** 689 * The DataHanderDataSource class implements the 690 * DataSource interface when the DataHandler is constructed 691 * with an Object and a mimeType string. 692 */ 693 class DataHandlerDataSource implements DataSource { 694 DataHandler dataHandler = null; 695 696 /** 697 * The constructor. 698 */ 699 public DataHandlerDataSource(DataHandler dh) { 700 this.dataHandler = dh; 701 } 702 703 /** 704 * Returns an {@code InputStream} representing this object. 705 * @return the {@code InputStream} 706 */ 707 public InputStream getInputStream() throws IOException { 708 return dataHandler.getInputStream(); 709 } 710 711 /** 712 * Returns the {@code OutputStream} for this object. 713 * @return the {@code OutputStream} 714 */ 715 public OutputStream getOutputStream() throws IOException { 716 return dataHandler.getOutputStream(); 717 } 718 719 /** 720 * Returns the MIME type of the data represented by this object. 721 * @return the MIME type 722 */ 723 public String getContentType() { 724 return dataHandler.getContentType(); 725 } 726 727 /** 728 * Returns the name of this object. 729 * @return the name of this object 730 */ 731 public String getName() { 732 return dataHandler.getName(); // what else would it be? 733 } 734 } 735 736 /* 737 * DataSourceDataContentHandler 738 * 739 * This is a <i>private</i> DataContentHandler that wraps the real 740 * DataContentHandler in the case where the DataHandler was instantiated 741 * with a DataSource. 742 */ 743 class DataSourceDataContentHandler implements DataContentHandler { 744 private DataSource ds = null; 745 private DataFlavor transferFlavors[] = null; 746 private DataContentHandler dch = null; 747 748 /** 749 * The constructor. 750 */ 751 public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { 752 this.ds = ds; 753 this.dch = dch; 754 } 755 756 /** 757 * Return the DataFlavors for this {@code DataContentHandler}. 758 * @return the DataFlavors 759 */ 760 public DataFlavor[] getTransferDataFlavors() { 761 762 if (transferFlavors == null) { 763 if (dch != null) { // is there a dch? 764 transferFlavors = dch.getTransferDataFlavors(); 765 } else { 766 transferFlavors = new DataFlavor[1]; 767 transferFlavors[0] = 768 new ActivationDataFlavor(ds.getContentType(), 769 ds.getContentType()); 770 } 771 } 772 return transferFlavors; 773 } 774 775 /** 776 * Return the Transfer Data of type DataFlavor from InputStream. 777 * @param df the DataFlavor 778 * @param ds the DataSource 779 * @return the constructed Object 780 */ 781 public Object getTransferData(DataFlavor df, DataSource ds) throws 782 UnsupportedFlavorException, IOException { 783 784 if (dch != null) 785 return dch.getTransferData(df, ds); 786 else if (df.equals(getTransferDataFlavors()[0])) // only have one now 787 return ds.getInputStream(); 788 else 789 throw new UnsupportedFlavorException(df); 790 } 791 792 public Object getContent(DataSource ds) throws IOException { 793 794 if (dch != null) 795 return dch.getContent(ds); 796 else 797 return ds.getInputStream(); 798 } 799 800 /** 801 * Write the object to the output stream. 802 */ 803 public void writeTo(Object obj, String mimeType, OutputStream os) 804 throws IOException { 805 if (dch != null) 806 dch.writeTo(obj, mimeType, os); 807 else 808 throw new UnsupportedDataTypeException( 809 "no DCH for content type " + ds.getContentType()); 810 } 811 } 812 813 /* 814 * ObjectDataContentHandler 815 * 816 * This is a <i>private</i> DataContentHandler that wraps the real 817 * DataContentHandler in the case where the DataHandler was instantiated 818 * with an object. 819 */ 820 class ObjectDataContentHandler implements DataContentHandler { 821 private DataFlavor transferFlavors[] = null; 822 private Object obj; 823 private String mimeType; 824 private DataContentHandler dch = null; 825 826 /** 827 * The constructor. 828 */ 829 public ObjectDataContentHandler(DataContentHandler dch, 830 Object obj, String mimeType) { 831 this.obj = obj; 832 this.mimeType = mimeType; 833 this.dch = dch; 834 } 835 836 /** 837 * Return the DataContentHandler for this object. 838 * Used only by the DataHandler class. 839 */ 840 public DataContentHandler getDCH() { 841 return dch; 842 } 843 844 /** 845 * Return the DataFlavors for this {@code DataContentHandler}. 846 * @return the DataFlavors 847 */ 848 public synchronized DataFlavor[] getTransferDataFlavors() { 849 if (transferFlavors == null) { 850 if (dch != null) { 851 transferFlavors = dch.getTransferDataFlavors(); 852 } else { 853 transferFlavors = new DataFlavor[1]; 854 transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), 855 mimeType, mimeType); 856 } 857 } 858 return transferFlavors; 859 } 860 861 /** 862 * Return the Transfer Data of type DataFlavor from InputStream. 863 * @param df the DataFlavor 864 * @param ds the DataSource 865 * @return the constructed Object 866 */ 867 public Object getTransferData(DataFlavor df, DataSource ds) 868 throws UnsupportedFlavorException, IOException { 869 870 if (dch != null) 871 return dch.getTransferData(df, ds); 872 else if (df.equals(getTransferDataFlavors()[0])) // only have one now 873 return obj; 874 else 875 throw new UnsupportedFlavorException(df); 876 877 } 878 879 public Object getContent(DataSource ds) { 880 return obj; 881 } 882 883 /** 884 * Write the object to the output stream. 885 */ 886 public void writeTo(Object obj, String mimeType, OutputStream os) 887 throws IOException { 888 if (dch != null) 889 dch.writeTo(obj, mimeType, os); 890 else if (obj instanceof byte[]) 891 os.write((byte[])obj); 892 else if (obj instanceof String) { 893 OutputStreamWriter osw = new OutputStreamWriter(os); 894 osw.write((String)obj); 895 osw.flush(); 896 } else throw new UnsupportedDataTypeException( 897 "no object DCH for MIME type " + this.mimeType); 898 } 899 }