1 /* 2 * Copyright (c) 1997, 2017, 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 * @exception IOException for failures creating the OutputStream 330 * 331 * @see javax.activation.DataSource#getOutputStream 332 * @see javax.activation.URLDataSource 333 */ 334 public OutputStream getOutputStream() throws IOException { 335 if (dataSource != null) 336 return dataSource.getOutputStream(); 337 else 338 return null; 339 } 340 341 /** 342 * Return the DataFlavors in which this data is available. <p> 343 * 344 * Returns an array of DataFlavor objects indicating the flavors 345 * the data can be provided in. The array is usually ordered 346 * according to preference for providing the data, from most 347 * richly descriptive to least richly descriptive.<p> 348 * 349 * The DataHandler attempts to find a DataContentHandler that 350 * corresponds to the MIME type of the data. If one is located, 351 * the DataHandler calls the DataContentHandler's 352 * {@code getTransferDataFlavors} method. <p> 353 * 354 * If a DataContentHandler can <i>not</i> be located, and if the 355 * DataHandler was created with a DataSource (or URL), one 356 * DataFlavor is returned that represents this object's MIME type 357 * and the {@code java.io.InputStream} class. If the 358 * DataHandler was created with an object and a MIME type, 359 * getTransferDataFlavors returns one DataFlavor that represents 360 * this object's MIME type and the object's class. 361 * 362 * @return an array of data flavors in which this data can be transferred 363 * @see javax.activation.DataContentHandler#getTransferDataFlavors 364 */ 365 public synchronized DataFlavor[] getTransferDataFlavors() { 366 if (factory != oldFactory) // if the factory has changed, clear cache 367 transferFlavors = emptyFlavors; 368 369 // if it's not set, set it... 370 if (transferFlavors == emptyFlavors) 371 transferFlavors = getDataContentHandler().getTransferDataFlavors(); 372 373 if (transferFlavors == emptyFlavors) 374 return transferFlavors; 375 else 376 return transferFlavors.clone(); 377 378 } 379 380 /** 381 * Returns whether the specified data flavor is supported 382 * for this object.<p> 383 * 384 * This method iterates through the DataFlavors returned from 385 * {@code getTransferDataFlavors}, comparing each with 386 * the specified flavor. 387 * 388 * @param flavor the requested flavor for the data 389 * @return true if the data flavor is supported 390 * @see javax.activation.DataHandler#getTransferDataFlavors 391 */ 392 public boolean isDataFlavorSupported(DataFlavor flavor) { 393 DataFlavor[] lFlavors = getTransferDataFlavors(); 394 395 for (int i = 0; i < lFlavors.length; i++) { 396 if (lFlavors[i].equals(flavor)) 397 return true; 398 } 399 return false; 400 } 401 402 /** 403 * Returns an object that represents the data to be 404 * transferred. The class of the object returned is defined by the 405 * representation class of the data flavor.<p> 406 * 407 * <b>For DataHandler's created with DataSources or URLs:</b><p> 408 * 409 * The DataHandler attempts to locate a DataContentHandler 410 * for this MIME type. If one is found, the passed in DataFlavor 411 * and the type of the data are passed to its {@code getTransferData} 412 * method. If the DataHandler fails to locate a DataContentHandler 413 * and the flavor specifies this object's MIME type and the 414 * {@code java.io.InputStream} class, this object's InputStream 415 * is returned. 416 * Otherwise it throws an UnsupportedFlavorException. <p> 417 * 418 * <b>For DataHandler's created with Objects:</b><p> 419 * 420 * The DataHandler attempts to locate a DataContentHandler 421 * for this MIME type. If one is found, the passed in DataFlavor 422 * and the type of the data are passed to its getTransferData 423 * method. If the DataHandler fails to locate a DataContentHandler 424 * and the flavor specifies this object's MIME type and its class, 425 * this DataHandler's referenced object is returned. 426 * Otherwise it throws an UnsupportedFlavorException. 427 * 428 * @param flavor the requested flavor for the data 429 * @return the object 430 * @exception UnsupportedFlavorException if the data could not be 431 * converted to the requested flavor 432 * @exception IOException if an I/O error occurs 433 * @see javax.activation.ActivationDataFlavor 434 */ 435 public Object getTransferData(DataFlavor flavor) 436 throws UnsupportedFlavorException, IOException { 437 return getDataContentHandler().getTransferData(flavor, dataSource); 438 } 439 440 /** 441 * Set the CommandMap for use by this DataHandler. 442 * Setting it to {@code null} causes the CommandMap to revert 443 * to the CommandMap returned by the 444 * {@code CommandMap.getDefaultCommandMap} method. 445 * Changing the CommandMap, or setting it to {@code null}, 446 * clears out any data cached from the previous CommandMap. 447 * 448 * @param commandMap the CommandMap to use in this DataHandler 449 * 450 * @see javax.activation.CommandMap#setDefaultCommandMap 451 */ 452 public synchronized void setCommandMap(CommandMap commandMap) { 453 if (commandMap != currentCommandMap || commandMap == null) { 454 // clear cached values... 455 transferFlavors = emptyFlavors; 456 dataContentHandler = null; 457 458 currentCommandMap = commandMap; 459 } 460 } 461 462 /** 463 * Return the <i>preferred</i> commands for this type of data. 464 * This method calls the {@code getPreferredCommands} method 465 * in the CommandMap associated with this instance of DataHandler. 466 * This method returns an array that represents a subset of 467 * available commands. In cases where multiple commands for the 468 * MIME type represented by this DataHandler are present, the 469 * installed CommandMap chooses the appropriate commands. 470 * 471 * @return the CommandInfo objects representing the preferred commands 472 * 473 * @see javax.activation.CommandMap#getPreferredCommands 474 */ 475 public CommandInfo[] getPreferredCommands() { 476 if (dataSource != null) 477 return getCommandMap().getPreferredCommands(getBaseType(), 478 dataSource); 479 else 480 return getCommandMap().getPreferredCommands(getBaseType()); 481 } 482 483 /** 484 * Return all the commands for this type of data. 485 * This method returns an array containing all commands 486 * for the type of data represented by this DataHandler. The 487 * MIME type for the underlying data represented by this DataHandler 488 * is used to call through to the {@code getAllCommands} method 489 * of the CommandMap associated with this DataHandler. 490 * 491 * @return the CommandInfo objects representing all the commands 492 * 493 * @see javax.activation.CommandMap#getAllCommands 494 */ 495 public CommandInfo[] getAllCommands() { 496 if (dataSource != null) 497 return getCommandMap().getAllCommands(getBaseType(), dataSource); 498 else 499 return getCommandMap().getAllCommands(getBaseType()); 500 } 501 502 /** 503 * Get the command <i>cmdName</i>. Use the search semantics as 504 * defined by the CommandMap installed in this DataHandler. The 505 * MIME type for the underlying data represented by this DataHandler 506 * is used to call through to the {@code getCommand} method 507 * of the CommandMap associated with this DataHandler. 508 * 509 * @param cmdName the command name 510 * @return the CommandInfo corresponding to the command 511 * 512 * @see javax.activation.CommandMap#getCommand 513 */ 514 public CommandInfo getCommand(String cmdName) { 515 if (dataSource != null) 516 return getCommandMap().getCommand(getBaseType(), cmdName, 517 dataSource); 518 else 519 return getCommandMap().getCommand(getBaseType(), cmdName); 520 } 521 522 /** 523 * Return the data in its preferred Object form. <p> 524 * 525 * If the DataHandler was instantiated with an object, return 526 * the object. <p> 527 * 528 * If the DataHandler was instantiated with a DataSource, 529 * this method uses a DataContentHandler to return the content 530 * object for the data represented by this DataHandler. If no 531 * {@code DataContentHandler} can be found for the 532 * the type of this data, the DataHandler returns an 533 * InputStream for the data. 534 * 535 * @return the content. 536 * @exception IOException if an IOException occurs during 537 * this operation. 538 */ 539 public Object getContent() throws IOException { 540 if (object != null) 541 return object; 542 else 543 return getDataContentHandler().getContent(getDataSource()); 544 } 545 546 /** 547 * A convenience method that takes a CommandInfo object 548 * and instantiates the corresponding command, usually 549 * a JavaBean component. 550 * <p> 551 * This method calls the CommandInfo's {@code getCommandObject} 552 * method with the {@code ClassLoader} used to load 553 * the {@code javax.activation.DataHandler} class itself. 554 * 555 * @param cmdinfo the CommandInfo corresponding to a command 556 * @return the instantiated command object 557 */ 558 public Object getBean(CommandInfo cmdinfo) { 559 Object bean = null; 560 561 try { 562 // make the bean 563 ClassLoader cld = null; 564 // First try the "application's" class loader. 565 cld = SecuritySupport.getContextClassLoader(); 566 if (cld == null) 567 cld = this.getClass().getClassLoader(); 568 bean = cmdinfo.getCommandObject(this, cld); 569 } catch (IOException e) { 570 } catch (ClassNotFoundException e) { } 571 572 return bean; 573 } 574 575 /** 576 * Get the DataContentHandler for this DataHandler: <p> 577 * 578 * If a DataContentHandlerFactory is set, use it. 579 * Otherwise look for an object to serve DCH in the 580 * following order: <p> 581 * 582 * 1) if a factory is set, use it <p> 583 * 2) if a CommandMap is set, use it <p> 584 * 3) use the default CommandMap <p> 585 * 586 * In any case, wrap the real DataContentHandler with one of our own 587 * to handle any missing cases, fill in defaults, and to ensure that 588 * we always have a non-null DataContentHandler. 589 * 590 * @return the requested DataContentHandler 591 */ 592 private synchronized DataContentHandler getDataContentHandler() { 593 594 // make sure the factory didn't change 595 if (factory != oldFactory) { 596 oldFactory = factory; 597 factoryDCH = null; 598 dataContentHandler = null; 599 transferFlavors = emptyFlavors; 600 } 601 602 if (dataContentHandler != null) 603 return dataContentHandler; 604 605 String simpleMT = getBaseType(); 606 607 if (factoryDCH == null && factory != null) 608 factoryDCH = factory.createDataContentHandler(simpleMT); 609 610 if (factoryDCH != null) 611 dataContentHandler = factoryDCH; 612 613 if (dataContentHandler == null) { 614 if (dataSource != null) 615 dataContentHandler = getCommandMap(). 616 createDataContentHandler(simpleMT, dataSource); 617 else 618 dataContentHandler = getCommandMap(). 619 createDataContentHandler(simpleMT); 620 } 621 622 // getDataContentHandler always uses these 'wrapper' handlers 623 // to make sure it returns SOMETHING meaningful... 624 if (dataSource != null) 625 dataContentHandler = new DataSourceDataContentHandler( 626 dataContentHandler, 627 dataSource); 628 else 629 dataContentHandler = new ObjectDataContentHandler( 630 dataContentHandler, 631 object, 632 objectMimeType); 633 return dataContentHandler; 634 } 635 636 /** 637 * Use the MimeType class to extract the MIME type/subtype, 638 * ignoring the parameters. The type is cached. 639 */ 640 private synchronized String getBaseType() { 641 if (shortType == null) { 642 String ct = getContentType(); 643 try { 644 MimeType mt = new MimeType(ct); 645 shortType = mt.getBaseType(); 646 } catch (MimeTypeParseException e) { 647 shortType = ct; 648 } 649 } 650 return shortType; 651 } 652 653 /** 654 * Sets the DataContentHandlerFactory. The DataContentHandlerFactory 655 * is called first to find DataContentHandlers. 656 * The DataContentHandlerFactory can only be set once. 657 * <p> 658 * If the DataContentHandlerFactory has already been set, 659 * this method throws an Error. 660 * 661 * @param newFactory the DataContentHandlerFactory 662 * @exception Error if the factory has already been defined. 663 * 664 * @see javax.activation.DataContentHandlerFactory 665 */ 666 public static synchronized void setDataContentHandlerFactory( 667 DataContentHandlerFactory newFactory) { 668 if (factory != null) 669 throw new Error("DataContentHandlerFactory already defined"); 670 671 SecurityManager security = System.getSecurityManager(); 672 if (security != null) { 673 try { 674 // if it's ok with the SecurityManager, it's ok with me... 675 security.checkSetFactory(); 676 } catch (SecurityException ex) { 677 // otherwise, we also allow it if this code and the 678 // factory come from the same class loader (e.g., 679 // the JAF classes were loaded with the applet classes). 680 if (DataHandler.class.getClassLoader() != 681 newFactory.getClass().getClassLoader()) 682 throw ex; 683 } 684 } 685 factory = newFactory; 686 } 687 } 688 689 /** 690 * The DataHanderDataSource class implements the 691 * DataSource interface when the DataHandler is constructed 692 * with an Object and a mimeType string. 693 */ 694 class DataHandlerDataSource implements DataSource { 695 DataHandler dataHandler = null; 696 697 /** 698 * The constructor. 699 */ 700 public DataHandlerDataSource(DataHandler dh) { 701 this.dataHandler = dh; 702 } 703 704 /** 705 * Returns an {@code InputStream} representing this object. 706 * @return the {@code InputStream} 707 */ 708 public InputStream getInputStream() throws IOException { 709 return dataHandler.getInputStream(); 710 } 711 712 /** 713 * Returns the {@code OutputStream} for this object. 714 * @return the {@code OutputStream} 715 */ 716 public OutputStream getOutputStream() throws IOException { 717 return dataHandler.getOutputStream(); 718 } 719 720 /** 721 * Returns the MIME type of the data represented by this object. 722 * @return the MIME type 723 */ 724 public String getContentType() { 725 return dataHandler.getContentType(); 726 } 727 728 /** 729 * Returns the name of this object. 730 * @return the name of this object 731 */ 732 public String getName() { 733 return dataHandler.getName(); // what else would it be? 734 } 735 } 736 737 /* 738 * DataSourceDataContentHandler 739 * 740 * This is a <i>private</i> DataContentHandler that wraps the real 741 * DataContentHandler in the case where the DataHandler was instantiated 742 * with a DataSource. 743 */ 744 class DataSourceDataContentHandler implements DataContentHandler { 745 private DataSource ds = null; 746 private DataFlavor transferFlavors[] = null; 747 private DataContentHandler dch = null; 748 749 /** 750 * The constructor. 751 */ 752 public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { 753 this.ds = ds; 754 this.dch = dch; 755 } 756 757 /** 758 * Return the DataFlavors for this {@code DataContentHandler}. 759 * @return the DataFlavors 760 */ 761 public DataFlavor[] getTransferDataFlavors() { 762 763 if (transferFlavors == null) { 764 if (dch != null) { // is there a dch? 765 transferFlavors = dch.getTransferDataFlavors(); 766 } else { 767 transferFlavors = new DataFlavor[1]; 768 transferFlavors[0] = 769 new ActivationDataFlavor(ds.getContentType(), 770 ds.getContentType()); 771 } 772 } 773 return transferFlavors; 774 } 775 776 /** 777 * Return the Transfer Data of type DataFlavor from InputStream. 778 * @param df the DataFlavor 779 * @param ds the DataSource 780 * @return the constructed Object 781 */ 782 public Object getTransferData(DataFlavor df, DataSource ds) throws 783 UnsupportedFlavorException, IOException { 784 785 if (dch != null) 786 return dch.getTransferData(df, ds); 787 else if (df.equals(getTransferDataFlavors()[0])) // only have one now 788 return ds.getInputStream(); 789 else 790 throw new UnsupportedFlavorException(df); 791 } 792 793 public Object getContent(DataSource ds) throws IOException { 794 795 if (dch != null) 796 return dch.getContent(ds); 797 else 798 return ds.getInputStream(); 799 } 800 801 /** 802 * Write the object to the output stream. 803 */ 804 public void writeTo(Object obj, String mimeType, OutputStream os) 805 throws IOException { 806 if (dch != null) 807 dch.writeTo(obj, mimeType, os); 808 else 809 throw new UnsupportedDataTypeException( 810 "no DCH for content type " + ds.getContentType()); 811 } 812 } 813 814 /* 815 * ObjectDataContentHandler 816 * 817 * This is a <i>private</i> DataContentHandler that wraps the real 818 * DataContentHandler in the case where the DataHandler was instantiated 819 * with an object. 820 */ 821 class ObjectDataContentHandler implements DataContentHandler { 822 private DataFlavor transferFlavors[] = null; 823 private Object obj; 824 private String mimeType; 825 private DataContentHandler dch = null; 826 827 /** 828 * The constructor. 829 */ 830 public ObjectDataContentHandler(DataContentHandler dch, 831 Object obj, String mimeType) { 832 this.obj = obj; 833 this.mimeType = mimeType; 834 this.dch = dch; 835 } 836 837 /** 838 * Return the DataContentHandler for this object. 839 * Used only by the DataHandler class. 840 */ 841 public DataContentHandler getDCH() { 842 return dch; 843 } 844 845 /** 846 * Return the DataFlavors for this {@code DataContentHandler}. 847 * @return the DataFlavors 848 */ 849 public synchronized DataFlavor[] getTransferDataFlavors() { 850 if (transferFlavors == null) { 851 if (dch != null) { 852 transferFlavors = dch.getTransferDataFlavors(); 853 } else { 854 transferFlavors = new DataFlavor[1]; 855 transferFlavors[0] = new ActivationDataFlavor(obj.getClass(), 856 mimeType, mimeType); 857 } 858 } 859 return transferFlavors; 860 } 861 862 /** 863 * Return the Transfer Data of type DataFlavor from InputStream. 864 * @param df the DataFlavor 865 * @param ds the DataSource 866 * @return the constructed Object 867 */ 868 public Object getTransferData(DataFlavor df, DataSource ds) 869 throws UnsupportedFlavorException, IOException { 870 871 if (dch != null) 872 return dch.getTransferData(df, ds); 873 else if (df.equals(getTransferDataFlavors()[0])) // only have one now 874 return obj; 875 else 876 throw new UnsupportedFlavorException(df); 877 878 } 879 880 public Object getContent(DataSource ds) { 881 return obj; 882 } 883 884 /** 885 * Write the object to the output stream. 886 */ 887 public void writeTo(Object obj, String mimeType, OutputStream os) 888 throws IOException { 889 if (dch != null) 890 dch.writeTo(obj, mimeType, os); 891 else if (obj instanceof byte[]) 892 os.write((byte[])obj); 893 else if (obj instanceof String) { 894 OutputStreamWriter osw = new OutputStreamWriter(os); 895 osw.write((String)obj); 896 osw.flush(); 897 } else throw new UnsupportedDataTypeException( 898 "no object DCH for MIME type " + this.mimeType); 899 } 900 }