home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / beans / Beans.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  17.4 KB  |  586 lines

  1. /*
  2.  * @(#)Beans.java    1.40 98/03/18
  3.  *
  4.  * Copyright 1996, 1997 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.beans;
  16.  
  17. import java.applet.*;
  18.  
  19. import java.awt.*;
  20.  
  21. import java.beans.AppletInitializer;
  22.  
  23. import java.beans.beancontext.BeanContext;
  24.  
  25. import java.io.*;
  26.  
  27. import java.lang.reflect.Constructor;
  28.  
  29. import java.net.URL;
  30. import java.lang.reflect.Array;
  31.  
  32. /**
  33.  * This class provides some general purpose beans control methods.
  34.  */
  35.  
  36. public class Beans {
  37.  
  38.     /**
  39.      * <p>
  40.      * Instantiate a JavaBean.
  41.      * </p>
  42.      *
  43.      * @param     classLoader the class-loader from which we should create
  44.      *                       the bean.  If this is null, then the system
  45.      *                        class-loader is used.
  46.      * @param     beanName    the name of the bean within the class-loader.
  47.      *                     For example "sun.beanbox.foobah"
  48.      *
  49.      * @exception java.lang.ClassNotFoundException if the class of a serialized
  50.      *              object could not be found.
  51.      * @exception java.io.IOException if an I/O error occurs.
  52.      */
  53.  
  54.     public static Object instantiate(ClassLoader cls, String beanName) throws java.io.IOException, ClassNotFoundException {
  55.     return Beans.instantiate(cls, beanName, null, null);
  56.     }
  57.  
  58.     /**
  59.      * <p>
  60.      * Instantiate a JavaBean.
  61.      * </p>
  62.      *
  63.      * @param     classLoader the class-loader from which we should create
  64.      *                       the bean.  If this is null, then the system
  65.      *                        class-loader is used.
  66.      * @param     beanName    the name of the bean within the class-loader.
  67.      *                     For example "sun.beanbox.foobah"
  68.      * @param     beanContext The BeanContext in which to nest the new bean
  69.      *
  70.      * @exception java.lang.ClassNotFoundException if the class of a serialized
  71.      *              object could not be found.
  72.      * @exception java.io.IOException if an I/O error occurs.
  73.      */
  74.  
  75.     public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext) throws java.io.IOException, ClassNotFoundException {
  76.     return Beans.instantiate(cls, beanName, beanContext, null);
  77.     }
  78.  
  79.     /**
  80.      * Instantiate a bean.
  81.      * <p>
  82.      * The bean is created based on a name relative to a class-loader.
  83.      * This name should be a dot-separated name such as "a.b.c".
  84.      * <p>
  85.      * In Beans 1.0 the given name can indicate either a serialized object
  86.      * or a class.  Other mechanisms may be added in the future.  In
  87.      * beans 1.0 we first try to treat the beanName as a serialized object
  88.      * name then as a class name.
  89.      * <p>
  90.      * When using the beanName as a serialized object name we convert the
  91.      * given beanName to a resource pathname and add a trailing ".ser" suffix.
  92.      * We then try to load a serialized object from that resource.
  93.      * <p>
  94.      * For example, given a beanName of "x.y", Beans.instantiate would first
  95.      * try to read a serialized object from the resource "x/y.ser" and if
  96.      * that failed it would try to load the class "x.y" and create an
  97.      * instance of that class.
  98.      * <p>
  99.      * If the bean is a subtype of java.applet.Applet, then it is given
  100.      * some special initialization.  First, it is supplied with a default
  101.      * AppletStub and AppletContext.  Second, if it was instantiated from 
  102.      * a classname the applet's "init" method is called.  (If the bean was
  103.      * deserialized this step is skipped.) 
  104.      * <p>
  105.      * Note that for beans which are applets, it is the caller's responsiblity
  106.      * to call "start" on the applet.  For correct behaviour, this should be done
  107.      * after the applet has been added into a visible AWT container.
  108.      * <p>
  109.      * Note that applets created via beans.instantiate run in a slightly
  110.      * different environment than applets running inside browsers.  In
  111.      * particular, bean applets have no access to "parameters", so they may 
  112.      * wish to provide property get/set methods to set parameter values.  We
  113.      * advise bean-applet developers to test their bean-applets against both
  114.      * the JDK appletviewer (for a reference browser environment) and the
  115.      * BDK BeanBox (for a reference bean container).
  116.      * 
  117.      * @param     classLoader the class-loader from which we should create
  118.      *                       the bean.  If this is null, then the system
  119.      *                        class-loader is used.
  120.      * @param     beanName    the name of the bean within the class-loader.
  121.      *                     For example "sun.beanbox.foobah"
  122.      * @param     beanContext The BeanContext in which to nest the new bean
  123.      * @param     initializer The AppletInitializer for the new bean
  124.      *
  125.      * @exception java.lang.ClassNotFoundException if the class of a serialized
  126.      *              object could not be found.
  127.      * @exception java.io.IOException if an I/O error occurs.
  128.      */
  129.  
  130.     public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer) 
  131.             throws java.io.IOException, ClassNotFoundException {
  132.  
  133.     java.io.InputStream ins;
  134.     java.io.ObjectInputStream oins = null;
  135.     Object result = null;
  136.     boolean serialized = false;
  137.     java.io.IOException serex = null;
  138.  
  139.     // Try to find a serialized object with this name
  140.     String serName = beanName.replace('.','/').concat(".ser");
  141.     if (cls == null) {
  142.         ins = ClassLoader.getSystemResourceAsStream(serName);
  143.     } else {
  144.         ins  = cls.getResourceAsStream(serName);
  145.     }
  146.     if (ins != null) {
  147.         try {
  148.             if (cls == null) {
  149.             oins = new ObjectInputStream(ins);
  150.             } else {
  151.             oins = new ObjectInputStreamWithLoader(ins, cls);
  152.             }
  153.             result = oins.readObject();
  154.         serialized = true;
  155.             oins.close();
  156.         } catch (java.io.IOException ex) {
  157.         ins.close();
  158.         // Drop through and try opening the class.  But remember
  159.         // the exception in case we can't find the class either.
  160.         serex = ex;
  161.         } catch (ClassNotFoundException ex) {
  162.         ins.close();
  163.         throw ex;
  164.         }
  165.     }
  166.  
  167.     if (result == null) {
  168.         // No serialized object, try just instantiating the class
  169.         Class cl;
  170.  
  171.         try {
  172.             if (cls == null) {
  173.                 cl = Class.forName(beanName);
  174.             } else {
  175.                 cl = cls.loadClass(beanName);
  176.             }
  177.         } catch (ClassNotFoundException ex) {
  178.         // There is no appropriate class.  If we earlier tried to
  179.         // deserialize an object and got an IO exception, throw that,
  180.         // otherwise rethrow the ClassNotFoundException.
  181.         if (serex != null) {
  182.             throw serex;
  183.         }
  184.         throw ex;
  185.         }
  186.  
  187.         /*
  188.          * Try to instantiate the class.
  189.          */
  190.  
  191.         try {
  192.             result = cl.newInstance();
  193.         } catch (Exception ex) {
  194.             throw new ClassNotFoundException();
  195.         }
  196.     }
  197.  
  198.     if (result != null) { 
  199.  
  200.         // Ok, if the result is an applet initialize it.
  201.  
  202.         AppletStub stub = null;
  203.  
  204.         if (result instanceof Applet) {
  205.             Applet  applet      = (Applet) result;
  206.         boolean needDummies = initializer == null;
  207.  
  208.         if (needDummies) {
  209.  
  210.                 // Figure our the codebase and docbase URLs.  We do this
  211.                 // by locating the URL for a known resource, and then
  212.                 // massaging the URL.
  213.         
  214.                 // First find the "resource name" corresponding to the bean
  215.                 // itself.  So a serialzied bean "a.b.c" would imply a
  216.             // resource name of "a/b/c.ser" and a classname of "x.y"
  217.             // would imply a resource name of "x/y.class".
  218.         
  219.                 String resourceName;
  220.  
  221.                 if (serialized) {
  222.                 // Serialized bean
  223.                 resourceName = beanName.replace('.','/').concat(".ser");
  224.                 } else {
  225.                 // Regular class
  226.                 resourceName = beanName.replace('.','/').concat(".class");
  227.                 }
  228.  
  229.                 URL objectUrl = null;
  230.                 URL codeBase  = null;
  231.                 URL docBase   = null;
  232.         
  233.                 // Now get the URL correponding to the resource name.
  234.  
  235.                 if (cls == null) {
  236.                 objectUrl = ClassLoader.getSystemResource(resourceName);
  237.                 } else {
  238.                 objectUrl = cls.getResource(resourceName);
  239.                 }
  240.         
  241.                 // If we found a URL, we try to locate the docbase by taking
  242.                 // of the final path name component, and the code base by taking
  243.                    // of the complete resourceName.
  244.                 // So if we had a resourceName of "a/b/c.class" and we got an
  245.                 // objectURL of "file://bert/classes/a/b/c.class" then we would
  246.                 // want to set the codebase to "file://bert/classes/" and the
  247.                 // docbase to "file://bert/classes/a/b/"
  248.         
  249.                 if (objectUrl != null) {
  250.                 String s = objectUrl.toExternalForm();
  251.  
  252.                 if (s.endsWith(resourceName)) {
  253.                       int ix   = s.length() - resourceName.length();
  254.                     codeBase = new URL(s.substring(0,ix));
  255.                     docBase  = codeBase;
  256.  
  257.                     ix = s.lastIndexOf('/');
  258.  
  259.                     if (ix >= 0) {
  260.                         docBase = new URL(s.substring(0,ix+1));
  261.                     }
  262.                 }
  263.                 }
  264.                         
  265.                 // Setup a default context and stub.
  266.                 BeansAppletContext context = new BeansAppletContext(applet);
  267.  
  268.                 stub = (AppletStub)new BeansAppletStub(applet, context, codeBase, docBase);
  269.                 applet.setStub(stub);
  270.         } else {
  271.             initializer.initialize(applet, beanContext);
  272.         }
  273.  
  274.             // now, if there is a BeanContext, add the bean, if applicable.
  275.  
  276.             if (beanContext != null) {
  277.                 beanContext.add(result);
  278.             }
  279.  
  280.         // If it was deserialized then it was already init-ed.
  281.         // Otherwise we need to initialize it.
  282.  
  283.         if (!serialized) {
  284.             // We need to set a reasonable initial size, as many
  285.             // applets are unhappy if they are started without 
  286.             // having been explicitly sized.
  287.             applet.setSize(100,100);
  288.             applet.init();
  289.         }
  290.  
  291.         if (needDummies) {
  292.           ((BeansAppletStub)stub).active = true;
  293.          } else initializer.activate(applet);
  294.  
  295.         } else if (beanContext != null) beanContext.add(result);
  296.     } 
  297.  
  298.     return result;
  299.     }
  300.  
  301.  
  302.     /**
  303.      * From a given bean, obtain an object representing a specified
  304.      * type view of that source object. 
  305.      * <p>
  306.      * The result may be the same object or a different object.  If
  307.      * the requested target view isn't available then the given
  308.      * bean is returned.
  309.      * <p>
  310.      * This method is provided in Beans 1.0 as a hook to allow the
  311.      * addition of more flexible bean behaviour in the future.
  312.      *
  313.      * @param obj  Object from which we want to obtain a view.
  314.      * @param targetType  The type of view we'd like to get.
  315.      *
  316.      */
  317.     public static Object getInstanceOf(Object bean, Class targetType) {
  318.         return bean;
  319.     }
  320.  
  321.     /**
  322.      * Check if a bean can be viewed as a given target type.
  323.      * The result will be true if the Beans.getInstanceof method
  324.      * can be used on the given bean to obtain an object that
  325.      * represents the specified targetType type view.
  326.      *
  327.      * @param bean  Bean from which we want to obtain a view.
  328.      * @param targetType  The type of view we'd like to get.
  329.      * @return "true" if the given bean supports the given targetType.
  330.      *
  331.      */
  332.     public static boolean isInstanceOf(Object bean, Class targetType) {
  333.     return Introspector.isSubclass(bean.getClass(), targetType);
  334.     }
  335.  
  336.  
  337.     /**
  338.      * Test if we are in design-mode.
  339.      *
  340.      * @return  True if we are running in an application construction
  341.      *        environment.
  342.      *
  343.      * @see java.beans.DesignMode
  344.      */
  345.     public static boolean isDesignTime() {
  346.     return designTime;
  347.     }
  348.  
  349.     /**
  350.      * @return  True if we are running in an environment where beans
  351.      *       can assume that an interactive GUI is available, so they 
  352.      *       can pop up dialog boxes, etc.  This will normally return
  353.      *       true in a windowing environment, and will normally return
  354.      *       false in a server environment or if an application is
  355.      *       running as part of a batch job.
  356.      *
  357.      * @see java.beans.VisibilityState
  358.      *
  359.      */
  360.     public static boolean isGuiAvailable() {
  361.     return guiAvailable;
  362.     }
  363.  
  364.     /**
  365.      * Used to indicate whether of not we are running in an application
  366.      * builder environment.  Note that this method is security checked
  367.      * and is not available to (for example) untrusted applets.
  368.      *
  369.      * @param isDesignTime  True if we're in an application builder tool.
  370.      */
  371.  
  372.     public static void setDesignTime(boolean isDesignTime)
  373.             throws SecurityException {
  374.     designTime = isDesignTime;
  375.     }
  376.  
  377.     /**
  378.      * Used to indicate whether of not we are running in an environment
  379.      * where GUI interaction is available.  Note that this method is 
  380.      * security checked and is not available to (for example) untrusted
  381.      * applets.
  382.      *
  383.      * @param isGuiAvailable  True if GUI interaction is available.
  384.      */
  385.  
  386.     public static void setGuiAvailable(boolean isGuiAvailable)
  387.             throws SecurityException {
  388.     guiAvailable = isGuiAvailable;
  389.     }
  390.  
  391.  
  392.     private static boolean designTime;
  393.     private static boolean guiAvailable = true;
  394. }
  395.  
  396. /**
  397.  * This subclass of ObjectInputStream delegates loading of classes to
  398.  * an existing ClassLoader.
  399.  */
  400.  
  401. class ObjectInputStreamWithLoader extends ObjectInputStream
  402. {
  403.     private ClassLoader loader;
  404.  
  405.     /**
  406.      * Loader must be non-null;
  407.      */
  408.  
  409.     public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader)
  410.         throws IOException, StreamCorruptedException {
  411.  
  412.     super(in);
  413.     if (loader == null) {
  414.             throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader");
  415.     }
  416.     this.loader = loader;
  417.     }
  418.  
  419.     /**
  420.      * Make a primitive array class
  421.      */
  422.  
  423.     private Class primitiveType(char type) {
  424.     switch (type) {
  425.     case 'B': return byte.class;
  426.         case 'C': return char.class;
  427.     case 'D': return double.class;
  428.     case 'F': return float.class;
  429.     case 'I': return int.class;
  430.     case 'J': return long.class;
  431.     case 'S': return short.class;
  432.     case 'Z': return boolean.class;
  433.     default: return null;
  434.     }
  435.     }
  436.  
  437.     /**
  438.      * Use the given ClassLoader rather than using the system class
  439.      */
  440.     protected Class resolveClass(ObjectStreamClass classDesc)
  441.     throws IOException, ClassNotFoundException {
  442.  
  443.     String cname = classDesc.getName();
  444.     if (cname.startsWith("[")) {
  445.         // An array
  446.         Class component;        // component class
  447.         int dcount;            // dimension
  448.         for (dcount=1; cname.charAt(dcount)=='['; dcount++) ;
  449.         if (cname.charAt(dcount) == 'L') {
  450.         component = loader.loadClass(cname.substring(dcount+1,
  451.                                  cname.length()-1));
  452.         } else {
  453.         if (cname.length() != dcount+1) {
  454.             throw new ClassNotFoundException(cname);// malformed
  455.         }
  456.         component = primitiveType(cname.charAt(dcount));
  457.         }
  458.         int dim[] = new int[dcount];
  459.         for (int i=0; i<dcount; i++) {
  460.         dim[i]=0;
  461.         }
  462.         return Array.newInstance(component, dim).getClass();
  463.     } else {
  464.         return loader.loadClass(cname);
  465.     }
  466.     }
  467. }
  468.  
  469. /**
  470.  * Package private support class.  This provides a default AppletContext
  471.  * for beans which are applets.
  472.  */
  473.  
  474. class BeansAppletContext implements AppletContext {
  475.     Applet target;
  476.     java.util.Hashtable imageCache = new java.util.Hashtable();
  477.  
  478.     BeansAppletContext(Applet target) {
  479.         this.target = target;
  480.     }
  481.  
  482.     public AudioClip getAudioClip(URL url) {
  483.     // We don't currently support audio clips in the Beans.instantiate
  484.     // applet context, unless by some luck there exists a URL content
  485.     // class that can generate an AudioClip from the audio URL.
  486.     try {
  487.         return (AudioClip) url.getContent();
  488.       } catch (Exception ex) {
  489.         return null;
  490.     }
  491.     }
  492.  
  493.     public synchronized Image getImage(URL url) {
  494.     Object o = imageCache.get(url);
  495.     if (o != null) {
  496.         return (Image)o;
  497.     }
  498.     try {
  499.         o = url.getContent();
  500.         if (o == null) {
  501.         return null;
  502.         }
  503.         if (o instanceof Image) {
  504.         imageCache.put(url, o);
  505.         return (Image) o;
  506.         }
  507.         // Otherwise it must be an ImageProducer.
  508.         Image img = target.createImage((java.awt.image.ImageProducer)o);
  509.         imageCache.put(url, img);
  510.         return img;
  511.  
  512.       } catch (Exception ex) {
  513.         return null;
  514.     }
  515.     }
  516.  
  517.     public Applet getApplet(String name) {
  518.     return null;
  519.     }
  520.  
  521.     public java.util.Enumeration getApplets() {
  522.     java.util.Vector applets = new java.util.Vector();
  523.     applets.addElement(target);
  524.     return applets.elements();    
  525.     }
  526.  
  527.     public void showDocument(URL url) {
  528.     // We do nothing.
  529.     }
  530.  
  531.     public void showDocument(URL url, String target) {
  532.     // We do nothing.
  533.     }
  534.  
  535.     public void showStatus(String status) {
  536.     // We do nothing.
  537.     }
  538. }
  539.  
  540. /**
  541.  * Package private support class.  This provides an AppletStub
  542.  * for beans which are applets.
  543.  */
  544. class BeansAppletStub implements AppletStub {
  545.     transient boolean active;
  546.     transient Applet target;
  547.     transient AppletContext context;
  548.     transient URL codeBase;
  549.     transient URL docBase;
  550.  
  551.     BeansAppletStub(Applet target,
  552.         AppletContext context, URL codeBase, 
  553.                 URL docBase) {
  554.         this.target = target;
  555.     this.context = context;
  556.     this.codeBase = codeBase;
  557.     this.docBase = docBase;
  558.     }
  559.  
  560.     public boolean isActive() {
  561.     return active;
  562.     }
  563.     
  564.     public URL getDocumentBase() {
  565.     // use the root directory of the applet's class-loader
  566.     return docBase;
  567.     }
  568.  
  569.     public URL getCodeBase() {
  570.     // use the directory where we found the class or serialized object.
  571.     return codeBase;
  572.     }
  573.  
  574.     public String getParameter(String name) {
  575.     return null;
  576.     }
  577.  
  578.     public AppletContext getAppletContext() {
  579.     return context;
  580.     }
  581.  
  582.     public void appletResize(int width, int height) {
  583.     // we do nothing.
  584.     }
  585. }
  586.