All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.loaders.InstanceSupport Maven / Gradle / Ivy

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.loaders;

import java.lang.reflect.*;
import java.io.*;

import org.openide.ErrorManager;
import org.openide.cookies.InstanceCookie;
import org.openide.filesystems.FileObject;
import org.openide.util.HelpCtx;

// imports for findHelp:
import java.beans.*;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.PropertyPermission;
import org.openide.util.SharedClassObject;
import org.openide.util.Lookup;

// Encapsulates working with classes and optimize it.
/** An instance cookie implementation that works with files or entries.
*
* @author   Jan Jancura, Jaroslav Tulach
*/
public class InstanceSupport extends Object implements InstanceCookie.Of {
    /** entry to work with */
    private MultiDataObject.Entry entry;

    /** throw exception during loading of the class */
    private Throwable clazzException;

    /** the class of the instance */
    private Class clazz;

    /** the class is applet */
    private Boolean applet;
    /** the class is bean */
    private Boolean bean;

    /** New support for given entry. The file is taken from the
    * entry and is updated if the entry moves or renames itself.
    * @param entry entry to create instance from
    */
    public InstanceSupport(MultiDataObject.Entry entry) {
        this.entry = entry;
    }
    
    /** Accessor for the entry. Needed in InstanceDataObject.Ser
     * @return the entry
     */
    MultiDataObject.Entry entry () {
        return entry;
    }
    

    // main methods .........................................................................................................

    /* The bean name for the instance.
    * @return the name for the instance
    */
    public String instanceName () {
        // XXX does this make any sense? Is this method useful for anything?
        String p = instanceOrigin().getPath();
        int x = p.lastIndexOf('.');
        if (x != -1 && x > p.lastIndexOf('/')) {
            p = p.substring(0, x);
        }
        return p.replace('/', '.');
    }

    /* The class of the instance represented by this cookie.
    * Can be used to test whether the instance is of valid
    * class before it is created.
    *
    * 

Note that SecurityException could be thrown * if an attempt was made e.g. to create an instance of a class * in a java.* package. Clients of InstanceSupport * which expect that this might happen (e.g. creating instances of * freeform user classes) should explicitly catch security exceptions * and convert them into whatever else as needed. * * @return the class of the instance * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { return instanceClass(null); } final Class instanceClass (ClassLoader cl) throws java.io.IOException, ClassNotFoundException { if (clazzException != null) { if (clazzException instanceof IOException) throw (IOException)clazzException; else if (clazzException instanceof ClassNotFoundException) throw (ClassNotFoundException)clazzException; else throw (ThreadDeath)clazzException; } if (clazz != null) return clazz; //System.out.println ("getClass " + fileName ); // NOI18N try { if (isSerialized ()) { // NOI18N // read class from ser file InputStream is = instanceOrigin ().getInputStream (); try { clazz = readClass (is); return clazz; } finally { is.close (); } } else { // find class by class loader clazz = findClass (instanceName (), cl); if (clazz == null) throw new ClassNotFoundException (instanceName()); return clazz; } } catch (IOException ex) { ErrorManager.getDefault().annotate (ex, ErrorManager.UNKNOWN, "From file: " + entry.getFile(), null, null, null); // NOI18N clazzException = ex; throw ex; } catch (ClassNotFoundException ex) { ErrorManager.getDefault().annotate (ex, ErrorManager.UNKNOWN, "From file: " + entry.getFile(), null, null, null); // NOI18N clazzException = ex; throw ex; } catch (RuntimeException re) { // turn other throwables into class not found ex. clazzException = new ClassNotFoundException("From file: " + entry.getFile() + " due to: " + re.toString()); // NOI18N ErrorManager.getDefault ().annotate (clazzException, re); throw (ClassNotFoundException) clazzException; } catch (LinkageError le) { clazzException = new ClassNotFoundException("From file: " + entry.getFile() + " due to: " + le.toString()); // NOI18N ErrorManager.getDefault ().annotate (clazzException, le); throw (ClassNotFoundException) clazzException; } } /*Query to found out if the object created by this cookie is * instance of given type. Does: *

    *    Class actualClass = instanceClass ();
    *    result = type.isAsignableFrom (actualClass);
    * 
* * @param type the class type we want to check * @return true if this cookie can produce object of given type */ public boolean instanceOf (Class type) { try { return type.isAssignableFrom (instanceClass ()); } catch (IOException ex) { return false; } catch (ClassNotFoundException ex) { return false; } } /** Returns the origin of the instance. * @return the origin */ public FileObject instanceOrigin () { // return getEntry ().getFile (); return entry.getFile (); } /* * @return an object to work with * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { try { if (isSerialized ()) { // create from ser file BufferedInputStream bis = new BufferedInputStream(instanceOrigin().getInputStream(), 1024); org.openide.util.io.NbObjectInputStream nbis = new org.openide.util.io.NbObjectInputStream(bis); Object o = nbis.readObject(); nbis.close(); return o; } else { Class c = instanceClass (); if (SharedClassObject.class.isAssignableFrom (c)) { // special support return SharedClassObject.findObject (c, true); } else { // create new instance return c.newInstance(); } } } catch (IOException ex) { // [PENDING] annotate with localized message ErrorManager.getDefault ().annotate (ex, instanceName ()); throw ex; } catch (ClassNotFoundException ex) { throw ex; } catch (Exception e) { // turn other throwables into class not found ex. throw new ClassNotFoundException(e.toString() + " from " + instanceName(), e); // NOI18N } catch (LinkageError e) { throw new ClassNotFoundException(e.toString() + " from " + instanceName(), e); // NOI18N } } /** Is this an applet? * @return true if this class is a java.applet.Applet * @deprecated This method probably should not be used, as it catches a variety of potentially * serious exceptions and errors, and swallows them so as to produce a simple boolean * result. (Notifying them all would be inappropriate as they typically come from user * code.) Better to directly parse the bytecode, using e.g. the classfile module, * which is immune to this class of errors. */ public boolean isApplet () { if (applet != null) return applet.booleanValue (); boolean b = instanceOf (java.applet.Applet.class); applet = b ? Boolean.TRUE : Boolean.FALSE; return b; } /** Is this a standalone executable? * @return true if this class has main method * (e.g., public static void main (String[] arguments)). * @deprecated This method probably should not be used, as it catches a variety of potentially * serious exceptions and errors, and swallows them so as to produce a simple boolean * result. (Notifying them all would be inappropriate as they typically come from user * code.) Better to directly parse the bytecode, using e.g. the classfile module, * which is immune to this class of errors. */ public boolean isExecutable () { try { Method main = instanceClass ().getDeclaredMethod ("main", new Class[] { // NOI18N String[].class }); int m = main.getModifiers (); return Modifier.isPublic (m) && Modifier.isStatic (m) && Void.TYPE.equals ( main.getReturnType () ); } catch (Exception ex) { return false; } catch (LinkageError re) { // false when other errors occur (NoClassDefFoundError etc...) return false; } } /** Is this a JavaBean? * @return true if this class represents JavaBean (is public and has a public default constructor). * @deprecated This method probably should not be used, as it catches a variety of potentially * serious exceptions and errors, and swallows them so as to produce a simple boolean * result. (Notifying them all would be inappropriate as they typically come from user * code.) Better to directly parse the bytecode, using e.g. the classfile module, * which is immune to this class of errors. */ public boolean isJavaBean () { if (bean != null) return bean.booleanValue (); // if from ser file => definitely it is a java bean if (isSerialized ()) { bean = Boolean.TRUE; return true; } // try to find out... try { Class clazz = instanceClass(); int modif = clazz.getModifiers (); if (!Modifier.isPublic (modif) || Modifier.isAbstract (modif)) { bean = Boolean.FALSE; return false; } Constructor c; try { c = clazz.getConstructor (new Class [0]); } catch (NoSuchMethodException e) { bean = Boolean.FALSE; return false; } if ((c == null) || !Modifier.isPublic (c.getModifiers ())) { bean = Boolean.FALSE; return false; } // check: if the class is an inner class, all outer classes have // to be public and in the static context: for (Class outer = clazz.getDeclaringClass(); outer != null; outer = outer.getDeclaringClass()) { // check if the enclosed class is static if (!Modifier.isStatic(modif)) return false; modif = outer.getModifiers(); // ... and the enclosing class is public if (!Modifier.isPublic(modif)) return false; } } catch (Exception ex) { bean = Boolean.FALSE; return true; } catch (LinkageError e) { // false when other errors occur (NoClassDefFoundError etc...) bean = Boolean.FALSE; return false; } // okay, this is bean... // return isBean = java.io.Serializable.class.isAssignableFrom (clazz); bean = Boolean.TRUE; return true; } /** Is this an interface? * @return true if the class is an interface * @deprecated This method probably should not be used, as it catches a variety of potentially * serious exceptions and errors, and swallows them so as to produce a simple boolean * result. (Notifying them all would be inappropriate as they typically come from user * code.) Better to directly parse the bytecode, using e.g. the classfile module, * which is immune to this class of errors. */ public boolean isInterface () { try { return instanceClass ().isInterface (); } catch (IOException ex) { return false; } catch (ClassNotFoundException cnfe) { return false; } } public String toString () { return instanceName (); } /** Find context help for some instance. * Helper method useful in nodes or data objects that provide an instance cookie; * they may choose to supply their own help context based on this. * All API classes which can provide help contexts will be tested for * (including HelpCtx instances themselves). * JComponents are checked for an attached help ID property, * as with {@link HelpCtx#findHelp(java.awt.Component)} (but not traversing parents). *

Also, partial compliance with the JavaHelp section on JavaBeans help is implemented--i.e., * if a Bean in its BeanInfo provides a BeanDescriptor which * has the attribute helpID, this will be returned. The value is not * defaulted (because it would usually be nonsense and would mask a useful default * help for the instance container), nor is the help set specification checked, * since someone should have installed the proper help set anyway, and the APIs * cannot add a new reference to a help set automatically. * See javax.help.HelpUtilities.getIDStringFromBean for details. *

Special IDs are added, corresponding to the class name, for all standard visual components. * @param instance the instance to check for help (it is permissible for the {@link InstanceCookie#instanceCreate} to return null) * @return the help context found on the instance or inferred from a Bean, * or null if none was found (or it was {@link HelpCtx#DEFAULT_HELP}) * * * @deprecated use org.openide.util.HelpCtx.findHelp (Object) */ public static HelpCtx findHelp (InstanceCookie instance) { try { Class clazz = instance.instanceClass(); // [a.n] I have moved the code here as those components's BeanInfo do not contain helpID // - it is faster // Help on some standard components. Note that borders/layout managers do not really work here. if (java.awt.Component.class.isAssignableFrom (clazz) || java.awt.MenuComponent.class.isAssignableFrom (clazz)) { String name = clazz.getName (); String[] pkgs = new String[] { "java.awt.", "javax.swing.", "javax.swing.border." }; // NOI18N for (int i = 0; i < pkgs.length; i++) { if (name.startsWith (pkgs[i]) && name.substring (pkgs[i].length ()).indexOf ('.') == -1) return new HelpCtx (name); } } Object o = instance.instanceCreate(); if (o != null) { HelpCtx h = HelpCtx.findHelp(o); if (h != HelpCtx.DEFAULT_HELP) { return h; } } } catch (Exception e) { ErrorManager.getDefault().notify(e); } return null; } /** Test whether the instance represents serialized version of a class * or not. * @return true if the file entry extension is ser */ private boolean isSerialized () { return instanceOrigin ().getExt ().equals ("ser"); // NOI18N } /** Reads a class from input stream. Expects a serialized object to be stored * in the stream and reads only a class from it. * @param is input stream to read from * @return the class of that stream * @exception IOException if something fails */ private Class readClass (InputStream is) throws IOException, ClassNotFoundException { /** object input stream */ class OIS extends ObjectInputStream { public OIS (InputStream iss) throws IOException { super (iss); } /** Throws exception to signal the kind of class found. */ public Class resolveClass (ObjectStreamClass osc) throws IOException, ClassNotFoundException { Class c = findClass (osc.getName (), null); if (c == writeRepl) { // if this is write replace of shared object then // continue in reading return c; } // stop the reading expecting that we have read the class // of the primary object throw new ClassEx (c); } }; ObjectInputStream ois = new OIS (new BufferedInputStream (is)); try { ois.readObject (); // should not happen throw new ClassNotFoundException (); } catch (ClassEx ex) { // good, we found the class return ex.clazz; } } /** the variable for access to SharedClassObject$WriteReplace */ private static Class writeRepl; static { try { writeRepl = Class.forName ("org.openide.util.SharedClassObject$WriteReplace"); // NOI18N } catch (Exception ex) { ex.printStackTrace(); } } /** Finds a class for given name. * @param name name of the class * @return the class for the name * @exception ClassNotFoundException if the class cannot be found */ private Class findClass (String name, ClassLoader customLoader) throws ClassNotFoundException { try { Class c; try { if (customLoader != null) { c = customLoader.loadClass(name); } else { // to save the space with wasting classloaders, try the system first ClassLoader loader = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class); if (loader == null) { loader = getClass ().getClassLoader (); } c = loader.loadClass (name); } } catch (ClassNotFoundException ex) { // ok, ignore and try our class loader c = createClassLoader().loadClass(name); } return c; } catch (ClassNotFoundException ex) { throw ex; } catch (RuntimeException ex) { throw ex; } catch (LinkageError le) { throw new ClassNotFoundException(le.toString(), le); } } /** Creates new NbClassLoader with restricted PermissionCollection * that contains only: * java.io.FilePermission("<>", "read") * java.util.PropertyPermission("*", "read") * * @return ClassLoader */ protected ClassLoader createClassLoader() { ClassLoader l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class); return l; } /** Trivial supporting instance cookie for already-existing objects. */ public static class Instance extends Object implements InstanceCookie.Of { /** the object to represent */ private Object obj; /** Create a new instance cookie. * @param obj the object to represent in this cookie */ public Instance (Object obj) { this.obj = obj; } /* The bean name for the instance. * @return the name for the instance */ public String instanceName () { return obj.getClass ().getName (); } /* The class of the instance represented by this cookie. * Can be used to test whether the instance is of valid * class before it is created. * * @return the class of the instance */ public Class instanceClass () { return obj.getClass (); } /* * @return an object to work with */ public Object instanceCreate () { return obj; } public boolean instanceOf (Class type) { return type.isAssignableFrom (instanceClass ()); } } /** The exception to use to signal succesful find of a class. * Used in method readClass. */ private static class ClassEx extends IOException { /** founded class */ public Class clazz; static final long serialVersionUID =4810039297880922426L; /** @param c the class */ public ClassEx (Class c) { clazz = c; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy