/*
* 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-2002 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide;
import java.beans.*;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
/** This class represents an abstract subclass for services
* (compilation, execution, debugging, etc.) that can be registered in
* the system.
*
* @author Jaroslav Tulach
*/
public abstract class ServiceType extends Object implements java.io.Serializable, HelpCtx.Provider {
/** generated Serialized Version UID */
private static final long serialVersionUID = -7573598174423654252L;
/** Name of property for the name of the service type. */
public static final String PROP_NAME = "name"; // NOI18N
/** name of the service type */
private String name;
/** listeners support */
private transient PropertyChangeSupport supp;
private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.ServiceType"); // NOI18N
/** Default human-presentable name of the service type.
* In the default implementation, taken from the bean descriptor.
* @return initial value of the human-presentable name
* @see FeatureDescriptor#getDisplayName
*/
protected String displayName () {
try {
return Introspector.getBeanInfo (getClass ()).getBeanDescriptor ().getDisplayName ();
} catch (Exception e) {
// Catching IntrospectionException, but also maybe NullPointerException...?
ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, e);
return getClass ().getName ();
}
}
/** Method that creates a cloned instance of this object. Subclasses
* are encouraged to implement the {@link Cloneable}
* interface, in such case the <code>clone</code> method is called as a result
* of calling this method. If the subclass does not implement
* <code>Cloneable</code>, it is serialized and deserialized,
* thus new instance created.
*
* @return new instance
* @exception IllegalStateException if something goes wrong, but should not happen
* @deprecated Service instance files should instead be copied in order to clone them.
*/
public final ServiceType createClone () {
Exception anEx;
if (this instanceof Cloneable) {
try {
return (ServiceType)clone ();
} catch (CloneNotSupportedException ex) {
anEx = ex;
}
} else {
try {
org.openide.util.io.NbMarshalledObject m = new org.openide.util.io.NbMarshalledObject (this);
return (ServiceType)m.get ();
} catch (IOException ex) {
anEx = ex;
} catch (ClassNotFoundException ex) {
anEx = ex;
}
}
// the code can get here only if an exception occured
// moreover it should never happen that this code is executed
IllegalStateException ex = new IllegalStateException ();
ErrorManager err = ErrorManager.getDefault ();
err.copyAnnotation(ex, anEx);
err.annotate (ex, "Cannot createClone for " + this); // NOI18N
throw ex;
}
/** Correctly implements the clone operation on this object. In
* order to work really correctly, the subclass has to implement the
* Cloneable interface.
*
* @return a new cloned instance that does not have any listeners
* @deprecated Service instance files should instead be copied in order to clone them.
*/
protected Object clone () throws CloneNotSupportedException {
ServiceType t = (ServiceType)super.clone ();
// clear listeners
t.supp = null;
// clear name
t.name = null;
return t;
}
/** Set the name of the service type.
* Usually it suffices to override {@link #displayName},
* or just to provide a {@link BeanDescriptor} for the class.
* @param name the new human-presentable name
*/
public void setName (String name) {
String old = this.name;
this.name = name;
if (supp != null) {
supp.firePropertyChange (PROP_NAME, old, name);
}
}
/** Get the name of the service type.
* The default value is given by {@link #displayName}.
* @return a human-presentable name for the service type
*/
public String getName () {
return name == null ? displayName () : name;
}
/** Get context help for this service type.
* @return context help
*/
public abstract HelpCtx getHelpCtx ();
/** Add a property change listener.
* @param l the listener to add
*/
public final synchronized void addPropertyChangeListener (PropertyChangeListener l) {
if (supp == null) supp = new PropertyChangeSupport (this);
supp.addPropertyChangeListener (l);
}
/** Remove a property change listener.
* @param l the listener to remove
*/
public final void removePropertyChangeListener (PropertyChangeListener l) {
if (supp != null) supp.removePropertyChangeListener (l);
}
/** Fire information about change of a property in the service type.
* @param name name of the property
* @param o old value
* @param n new value
*/
protected final void firePropertyChange (String name, Object o, Object n) {
if (supp != null) {
supp.firePropertyChange (name, o, n);
}
}
/** The registry of all services. This class is provided by the implementation
* of the IDE and should hold all of the services registered to the system.
* <P>
* This class can be serialized to securely save settings of all
* services in the system.
* @deprecated Use lookup instead.
*/
public static abstract class Registry implements java.io.Serializable {
/** suid */
final static long serialVersionUID = 8721000770371416481L;
/** Get all available services managed by the engine.
* @return an enumeration of {@link ServiceType}s
*/
public abstract Enumeration services ();
/** Get all available services that are assignable to the given superclass.
* @param clazz the class that all services should be subclass of
* @return an enumeration of all matching {@link ServiceType}s
*/
public Enumeration services (final Class clazz) {
return new org.openide.util.enums.FilterEnumeration (services ()) {
public boolean accept (Object o) {
return clazz.isInstance (o);
}
};
}
/** Getter for list of all service types.
* @return a list of {@link ServiceType}s
*/
public abstract java.util.List getServiceTypes ();
/** Setter for list of service types. This permits changing
* instances of the objects but only within the types that are already registered
* in the system by manifest sections. If an instance of any other type
* is in the list it is ignored.
*
* @param arr a list of {@link ServiceType}s
* @deprecated Better to change service instance files instead.
*/
public abstract void setServiceTypes (java.util.List arr);
/** Find the service type implemented as a given class.
* The whole registry is searched for a service type of that exact class (subclasses do not count).
* <P>
* This could be used during (de-)serialization
* of a service type: only store its class name
* and then try to find the type implemented by that class later.
*
* @param clazz the class of the service type looked for
* @return the desired type or <code>null</code> if it does not exist
* @deprecated Just use lookup.
*/
public ServiceType find (Class clazz) {
Enumeration en = services ();
while (en.hasMoreElements ()) {
Object o = en.nextElement ();
if (o.getClass () == clazz) {
return (ServiceType)o;
}
}
return null;
}
/** Find a service type of a supplied name in the registry.
* <P>
* This could be used during (de-)serialization
* of a service type: only store its name
* and then try to find the type later.
*
* @param name (display) name of service type to find
* @return the desired type or <code>null</code> if it does not exist
*/
public ServiceType find (String name) {
Enumeration en = services ();
while (en.hasMoreElements ()) {
ServiceType o = (ServiceType)en.nextElement ();
if (name.equals (o.getName ())) {
return o;
}
}
return null;
}
}
/** Handle for a service type. This is a serializable class that should be used
* to store types and to recreate them after deserialization.
*/
public static final class Handle extends Object
implements java.io.Serializable {
/** generated Serialized Version UID */
static final long serialVersionUID = 7233109534462148872L;
/** name executor */
private String name;
/** name of class of the executor */
private String className;
/** kept ServiceType may be <tt>null</tt> after deserialization */
private transient ServiceType serviceType;
/** Create a new handle for an service.
* @param ex the service to store a handle for
*/
public Handle (ServiceType ex) {
name = ex.getName ();
className = ex.getClass ().getName ();
serviceType = ex;
}
/** Find the service for this handle.
* @return the reconstituted service type, or <code>null</code> in case of problems
*/
public ServiceType getServiceType () {
if (serviceType == null) {
// the class to search for
Class clazz;
// the first subclass of ServiceType to search for
Class serviceTypeClass;
// try to find it by class
try {
clazz = Class.forName (className, true, (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class));
serviceTypeClass = clazz;
while (serviceTypeClass.getSuperclass () != ServiceType.class) {
serviceTypeClass = serviceTypeClass.getSuperclass ();
}
} catch (ClassNotFoundException ex) {
// #32140 - do not notify user about this exception. This exception
// should be only thrown when module providing the service
// was uninstalled and in that case the exception must be ignored.
err.log(ErrorManager.INFORMATIONAL, "Service not found: "+ex.toString()); //NOI18N
// nothing better to use
clazz = ServiceType.class;
serviceTypeClass = ServiceType.class;
}
// try to find the executor by name
ServiceType.Registry r = (ServiceType.Registry)Lookup.getDefault().lookup(ServiceType.Registry.class);
Enumeration en = r.services (clazz);
ServiceType some = r.find (clazz);
while (en.hasMoreElements ()) {
ServiceType t = (ServiceType)en.nextElement ();
if (!serviceTypeClass.isInstance (t)) {
// ignore non instances
continue;
}
String n = t.getName ();
if (n != null && n.equals (name)) {
return t;
}
// remember it for later use
if (some == null || (some.getClass () != clazz && t.getClass () == clazz)) {
// remember the best match
some = t;
}
}
// if clazz does not exist and there is no service with same name -> return null
if (serviceTypeClass == ServiceType.class)
return null;
return some;
}
return serviceType;
}
/** Old compatibility version.
*/
private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
name = (String)ois.readObject ();
String clazz = (String)ois.readObject ();
className = (clazz==null)? null: org.openide.util.Utilities.translate (clazz);
}
/** Has also save the object.
*/
private void writeObject (ObjectOutputStream oos) throws IOException {
oos.writeObject (name);
oos.writeObject (className);
}
// for debugging purposes
public String toString () {
return "Handle[" + className + ":" + name + "]"; // NOI18N
}
}
}