What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* 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.options;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import org.openide.ErrorManager;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.SharedClassObject;
/** Base class for all system options.
* Provides methods for adding
* and working with property change and guarantees
* that all instances of the same class will share these listeners.
*
* When a new option is created, it should subclass
* SystemOption , add static variables to it that will hold
* the values of properties, and write non-static setters/getters that will
* notify all listeners about property changes via
* {@link #firePropertyChange}.
* JavaBeans introspection is used to find the properties,
* so it is possible to use {@link BeanInfo}.
*
* @author Jaroslav Tulach
*/
public abstract class SystemOption extends SharedClassObject implements HelpCtx.Provider {
/** generated Serialized Version UID */
static final long serialVersionUID = 558589201969066966L;
/** property to indicate that the option is currently loading its data */
private static final Object PROP_LOADING = new Object ();
/** property to indicate that the option is currently loading its data */
private static final Object PROP_STORING = new Object ();
/** Default constructor. */
public SystemOption() {
// SystemOption must declare this property in order to be correctly deserialized
// by SharedClassObject.findObject function
putProperty ("netbeans.systemoption.hack", null); // NOI18N
}
/** Fire a property change event to all listeners. Delays
* this loading when readExternal is active till it finishes.
*
* @param name the name of the property
* @param oldValue the old value
* @param newValue the new value
*/
protected void firePropertyChange (
String name, Object oldValue, Object newValue
) {
if (getProperty (PROP_LOADING) != null) {
// somebody is loading, assign any object different than
// this to indicate that firing should occure
putProperty (PROP_LOADING, PROP_LOADING);
// but do not fire the change now
return;
}
super.firePropertyChange (name, oldValue, newValue);
}
/** Write all properties of this object (or subclasses) to an object output.
* @param out the output stream
* @exception IOException on error
*/
public void writeExternal (ObjectOutput out) throws IOException {
try {
// gets info about all properties that were added by subclass
BeanInfo info = org.openide.util.Utilities.getBeanInfo (getClass (), SystemOption.class);
PropertyDescriptor[] desc = info.getPropertyDescriptors ();
putProperty (PROP_STORING, this);
Object[] param = new Object[0];
synchronized (getLock ()) {
// write all properties that have getter to stream
for (int i = 0; i < desc.length; i++) {
// skip readonly Properties
if (desc[i].getWriteMethod () == null) {
continue;
}
String propName = desc[i].getName();
Object value = getProperty(propName);
boolean fromRead;
// JST: this code handles the case when somebody needs to store
// different value then is the value of get/set method.
// in such case value (from getProperty) is not of the type
// of the getter/setter and is used instead of the value from getXXXX
Method read = desc[i].getReadMethod();
if (read == null) {
continue;
}
if (value == null || isInstance(desc[i].getPropertyType(), value)) {
fromRead = true;
try {
value = read.invoke (this, param);
} catch (InvocationTargetException ex) {
// exception thrown
IOException ne = new IOException (NbBundle.getMessage (
SystemOption.class,
"EXC_InGetter",
getClass (),
desc[i].getName ()
));
ErrorManager.getDefault ().annotate (ne, ex);
throw ne;
} catch (IllegalAccessException ex) {
// exception thrown
IOException ne = new IOException (NbBundle.getMessage (
SystemOption.class,
"EXC_InGetter",
getClass (),
desc[i].getName ()
));
ErrorManager.getDefault ().annotate (ne, ex);
throw ne;
}
} else {
fromRead = false;
}
// writes name of the property
out.writeObject (propName);
// writes its value
out.writeObject (value);
// from getter or stored prop?
out.writeObject(fromRead ? Boolean.TRUE : Boolean.FALSE);
}
}
} catch (IntrospectionException ex) {
// if we cannot found any info about properties
} finally {
putProperty (PROP_STORING, null);
}
// write null to signal end of properties
out.writeObject (null);
}
/** Returns true if the object is assignable to the class.
* Also if the class is primitive and the object is of the matching wrapper type.
*/
private static boolean isInstance(Class c, Object o) {
return c.isInstance(o) ||
(c == Byte.TYPE && (o instanceof Byte)) ||
(c == Short.TYPE && (o instanceof Short)) ||
(c == Integer.TYPE && (o instanceof Integer)) ||
(c == Long.TYPE && (o instanceof Long)) ||
(c == Float.TYPE && (o instanceof Float)) ||
(c == Double.TYPE && (o instanceof Double)) ||
(c == Boolean.TYPE && (o instanceof Boolean)) ||
(c == Character.TYPE && (o instanceof Character));
}
/** Read all properties of this object (or subclasses) from an object input.
* If there is a problem setting the value of any property, that property will be ignored;
* other properties should still be set.
* @param in the input stream
* @exception IOException on error
* @exception ClassNotFound if a class used to restore the system option is not found
*/
public void readExternal (ObjectInput in)
throws IOException, ClassNotFoundException {
// hashtable that maps names of properties to setter methods
HashMap map = new HashMap ();
try {
synchronized (getLock ()) {
// indicate that we are loading files
putProperty (PROP_LOADING, this);
try {
// gets info about all properties that were added by subclass
BeanInfo info = org.openide.util.Utilities.getBeanInfo (getClass (), SystemOption.class);
PropertyDescriptor[] desc = info.getPropertyDescriptors ();
// write all properties that have getter to stream
for (int i = 0; i < desc.length; i++) {
Method m = desc[i].getWriteMethod ();
/*if (m == null) {
System.out.println ("HOW HOW HOW HOWHOWHOWHOWHWO: " + desc[i].getName() + " XXX " + getClass());
throw new IOException (new MessageFormat (NbBundle.getBundle (SystemOption.class).getString ("EXC_InSetter")).
format (new Object[] {getClass (), desc[i].getName ()})
);
} */
map.put (desc[i].getName (), m );
}
} catch (IntrospectionException ex) {
// if we cannot found any info about properties
// leave the hashtable empty and only read stream till null is found
ErrorManager.getDefault().notify (
ErrorManager.INFORMATIONAL, ex);
}
String preread = null;
do {
// read the name of property
String name;
if (preread != null) {
name = preread;
preread = null;
} else {
name = (String)in.readObject();
}
// break if the end of property stream is found
if (name == null) break;
// read the value of property
Object value = in.readObject ();
// read flag - use the setter method or store as property?
Object useMethodObject = in.readObject();
boolean useMethod;
boolean nullRead = false; // this should be last processed property?
if (useMethodObject == null) {
useMethod = true;
nullRead = true;
} else if (useMethodObject instanceof String) {
useMethod = true;
preread = (String) useMethodObject;
} else {
useMethod = ((Boolean) useMethodObject).booleanValue();
}
if (useMethod) {
// set the value
Method write = (Method)map.get (name);
if (write != null) {
// if you have where to set the value
try {
write.invoke (this, new Object[] { value });
} catch (Exception ex) {
String msg = "Cannot call " + write + " for property " + getClass().getName() + "." + name; // NOI18N
ErrorManager.getDefault().annotate(ex, ErrorManager.UNKNOWN, msg, null, null, null);
ErrorManager.getDefault ().notify (
ErrorManager.INFORMATIONAL,
ex
);
}
}
} else {
putProperty(name, value, false);
}
if (nullRead) {
break;
}
} while (true);
}
} finally {
// get current state
if (this != getProperty (PROP_LOADING)) {
// some changes should be fired
// loading finished
putProperty (PROP_LOADING, null);
firePropertyChange (null, null, null);
} else {
// loading finished
putProperty (PROP_LOADING, null);
}
}
}
protected boolean clearSharedData () {
return false;
}
/**
* Get the name of this system option.
* The default implementation just uses the {@link #displayName display name}.
* @return the name
*/
public final String getName () {
return displayName ();
}
/**
* Get the display name of this system option.
* @return the display name
*/
public abstract String displayName ();
/** Get context help for this system option.
* @return context help
*/
public HelpCtx getHelpCtx () {
return new HelpCtx (SystemOption.class);
}
/** Allows subclasses to test whether the change of a property
* is invoked from readExternal method or by external change invoked
* by any other program.
*
* @return true if the readExternal method is in progress
*/
protected final boolean isReadExternal () {
return getProperty (PROP_LOADING) != null;
}
/** Allows subclasses to test whether the getter of a property
* is invoked from writeExternal method or by any other part of the program.
*
* @return true if the writeExternal method is in progress
*/
protected final boolean isWriteExternal () {
return getProperty (PROP_STORING) != null;
}
}
|