|
What this is
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.netbeans.modules.java.ui.nodes.elements; import java.beans.*; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.ref.WeakReference; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.*; import org.openide.nodes.*; import org.openide.util.*; import org.openide.util.actions.SystemAction; import org.openide.ErrorManager; import org.openide.text.PositionBounds; import org.openide.cookies.OpenCookie; import org.openide.loaders.DataObject; import org.openide.src.ElementProperties; import org.netbeans.modules.java.ui.nodes.editors.*; import org.netbeans.modules.javacore.internalapi.JMIElementCookie; import org.netbeans.modules.java.JavaEditor; import org.netbeans.modules.javacore.internalapi.JavaMetamodel; import org.netbeans.jmi.javamodel.*; import org.netbeans.jmi.javamodel.Type; import org.netbeans.api.mdr.events.MDRChangeListener; import org.netbeans.api.mdr.events.MDRChangeEvent; import org.netbeans.api.mdr.events.AttributeEvent; import org.netbeans.api.mdr.events.MDRChangeSource; import javax.jmi.reflect.JmiException; import javax.swing.*; /** Superclass of nodes representing elements in the source hierarchy. *
true if this node should allow modifications.
* These include writable properties, clipboard operations, deletions, etc.
* @param formatName name of the element format property to listen to;
* null is possible; see {@link SourceOptions}.PROP_XXX_FORMAT
*/
public ElementNode(Element element, Children children, boolean writeable, String formatName) {
super(children);
this.element = element;
this.writeable = writeable;
setIconBase(resolveIconBase());
setDisplayName(getElementFormat().format(element));
if (element instanceof MDRChangeSource) {
wElementL = (JMIElementListener) createJMIElementListener();
((MDRChangeSource) element).addListener(wElementL);
}
displayFormat = null;
registerElementFormatName(formatName);
CookieSet cs = getCookieSet();
cs.add(new OpenCookieImpl(this)); // OpenCookie
cs.add(this); // JMIElementCookie
Resource r = this.element.getResource();
DataObject dobj = JavaMetamodel.getManager().getDataObject(r);
cs.add(dobj); // DataObject
}
/* Gets the short description of this node.
* @return A localized short description associated with this node.
*/
public String getShortDescription() {
try {
return getHintElementFormat().format(element);
}
catch (IllegalArgumentException e) {
return super.getShortDescription();
}
}
public void destroy() throws IOException {
boolean fail = true;
try {
JavaMetamodel.getDefaultRepository().beginTrans(true);
try {
element.refDelete();
fail = false;
} finally {
JavaMetamodel.getDefaultRepository().endTrans(fail);
}
} catch (JmiException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
super.destroy();
}
/** Get the currently appropriate icon base.
* Subclasses should make this sensitive to the state of the element--for example,
* a private variable may have a different icon than a public one.
* The icon will be automatically changed whenever a
* {@link #getIconAffectingProperties relevant} change is made to the element.
* @return icon base
* @see org.openide.nodes.AbstractNode#setIconBase
*/
abstract protected String resolveIconBase();
/** Get the names of all element properties which might affect the choice of icon.
* The default implementation just returns {@link #PROP_MODIFIERS}.
* @return the property names, from {@link org.openide.src.ElementProperties}
*/
protected String[] getIconAffectingProperties() {
return ICON_AFFECTING_PROPERTIES;
}
/** Get a format for the element's display name.
* The display name will be automatically updated whenever a
* {@link ElementFormat#dependsOnProperty relevant}
* change is made to the element.
* @return the format
*/
public final ElementFormat getElementFormat() {
return elementFormat;
}
/** Set the format for the display name.
* @param elementFormat the new format
* @throws java.lang.IllegalArgumentException if the format object is inappropriate
* for this type of Element. No assignment is made in such case.
*/
public final void setElementFormat(ElementFormat elementFormat) {
setDisplayName(elementFormat.format(this.element));
this.elementFormat = elementFormat;
}
/**
* subclasses implement this to supply persistent format
* @return element format
* @see SourceOptions
*/
protected abstract ElementFormat getElementFormatProperty();
/**
* Registers name of the element format property to listen to.
* @param formatName element format name
*/
private void registerElementFormatName(String formatName) {
this.formatListener = new ElementFormatListener(formatName);
SourceOptions sos = getSourceOptions();
PropertyChangeListener wFormatListener = WeakListeners.propertyChange(formatListener, sos);
sos.addPropertyChangeListener(wFormatListener);
}
/**
* keeps track of element format changes
*/
private final class ElementFormatListener implements PropertyChangeListener {
private final String formatName;
public ElementFormatListener(String formatName) {
this.formatName = formatName;
}
public void propertyChange(PropertyChangeEvent evt) {
String pname = evt.getPropertyName();
if (pname == null || pname.equals(formatName)) {
final ElementFormat newFormat = ElementNode.this.getElementFormatProperty();
// format has to run outside the awt-event thead; it touches jmi
RequestProcessor.getDefault().post(new Runnable() {
public void run() {
applyFormat(newFormat);
}
});
}
}
private void applyFormat(ElementFormat ef) {
ChangeDescriptor cd = new ChangeDescriptor();
try {
cd.displayName = ef.format(element);
} catch (IllegalArgumentException e) {
ef = getInvalidFormat();
cd.displayName = ef.format(element);
}
elementFormat = ef;
fireChangesInAWTThread(ElementNode.this, cd);
}
}
final void setElementFormat0(ElementFormat elementFormat) {
try {
setElementFormat(elementFormat);
} catch (IllegalArgumentException iae) {
setElementFormat(getInvalidFormat());
}
}
static ElementFormat getInvalidFormat() {
if (invalidFormat != null)
return invalidFormat;
return invalidFormat = new ElementFormat(getString("FMT_InvalidFormat")); // NOI18N
}
/** Get a format for creating this node's
* {@link java.beans.FeatureDescriptor#getShortDescription short description}.
*/
abstract protected ElementFormat getHintElementFormat();
/**
* Rename is a job for refactoring, so this implementation does not allow to rename node.
*
* @return false
*/
public boolean canRename() {
return false;
}
/** Test whether this node can be deleted.
* The default implementation assumes it can if this node is {@link #writeable}.
*
* @return true if this node can be renamed
*/
public boolean canDestroy () {
return isWriteable();
}
/** Test whether this node can be copied.
* The default implementation returns true .
* @return true if it can
*/
public boolean canCopy () {
return false;
}
/** Test whether this node can be cut.
* The default implementation assumes it can if this node is {@link #writeable}.
* @return true if it can
*/
public boolean canCut () {
return false;
// return isWriteable();
}
/** Set all actions for this node.
* @param actions new list of actions
* @param preferred default action
*/
public void setActions(SystemAction[] actions, SystemAction preferred) {
systemActions = actions;
this.preferredAction = preferred;
}
public Action getPreferredAction() {
Action a = preferredAction;
if (a == null) {
a = super.getPreferredAction();
}
return a;
}
/** Calls super.fireCookieChange. The reason why is redefined
* is only to allow the access from this package.
*/
void superFireCookieChange() {
fireCookieChange();
}
public Element getElement() {
return this.element;
}
/** Test for equality.
* @return true if the represented {@link org.openide.src.Element}s are equal
*/
public boolean equals (Object o) {
return (o instanceof ElementNode) && (element.equals (((ElementNode)o).element));
}
/** Get a hash code.
* @return the hash code from the represented {@link org.openide.src.Element}
*/
public int hashCode () {
return element.hashCode ();
}
boolean isWriteable() {
return writeable && SourceEditSupport.isWriteable(element);
}
void superSetName(String name) {
super.setName(name);
}
void superPropertyChange (String name, Object o, Object n) {
super.firePropertyChange (name, o, n);
}
void superShortDescriptionChange (String o, String n) {
super.fireShortDescriptionChange(o, n);
}
MDRChangeListener createJMIElementListener() {
JMIElementListener l = new JMIElementListener(this);
return l;
}
/**
* subclasses can extend default behavior that cares about displayName, name, shortDescription, iconBase using methods
* {@link ElementFormat#dependsOnProperty}, {@link #getIconAffectingProperties} and mapAttributeName.
* The method is run inside the read-only JMI transaction.
* @param ae attribute change event
* @return descriptor of changes
*/
protected ChangeDescriptor handleAttributeChange(AttributeEvent ae) {
final Object src = ae.getSource();
ChangeDescriptor cd = new ChangeDescriptor();
if (src != element || !((Element) src).isValid()) {
return cd;
}
// System.out.println("##ElementNode: " + ae.getAttributeName() + ", el: " + src.getClass() + ", lsnr: " + System.identityHashCode(this));
String attrName = ae.getAttributeName();
String propName = mapAttributeName(attrName);
if (propName == null) {
cd.displayName = getElementFormat().format(element);
cd.iconBase = resolveIconBase();
} else {
// display name
if (getElementFormat().dependsOnProperty(propName)) {
cd.displayName = getElementFormat().format(element);
}
// icon
String[] iconProps = getIconAffectingProperties();
for (int i = 0; i < iconProps.length; i++) {
if (iconProps[i].equals(propName)) {
cd.iconBase = resolveIconBase();
break;
}
}
if (propName.equals(ElementProperties.PROP_NAME)) {
cd.name = ((NamedElement) element).getName();
}
// tool tip
if (getHintElementFormat().dependsOnProperty(propName)) {
cd.shortDescription = getShortDescription();
}
}
return cd;
}
/**
* subclasses can extend default behavior that cares about displayName, name, shortDescription, iconBase.
* The method is run inside AWT-event thread
* @param desc descriptor of changes
* @see #handleAttributeChange
*/
protected void processChange(ChangeDescriptor desc) {
if (desc.displayName != null)
setDisplayName(desc.displayName);
if (desc.iconBase != null)
setIconBase(desc.iconBase);
if (desc.name != null)
superSetName(desc.name);
if (desc.shortDescription != null)
superShortDescriptionChange("", desc.shortDescription); // NOI18N
if (desc.sheet != null) {
setSheet(desc.sheet);
}
}
/**
* register property name to allow notification of its changes
* @return map of jmi attribute names to property names
*/
protected abstract Map getAttributeNameMap();
/** maps JMI attribute name to property name ({@link org.openide.src.ElementProperties}) */
final String mapAttributeName(String name) {
assert name != null;
String property = (String) getAttributeNameMap().get(name);
return (property == null)? name: property;
}
static String getString(String key) {
return NbBundle.getMessage(ElementNode.class, key);
}
private static void fireChangesInAWTThread(final ElementNode n, final ChangeDescriptor cd) {
Mutex.EVENT.writeAccess(new Runnable() {
public void run() {
n.processChange(cd);
}
});
}
private DataObject getDataObject() {
return (DataObject) this.getCookie(DataObject.class);
}
// ================== Element listener =================================
static final class JMIElementListener extends WeakReference implements MDRChangeListener, Runnable {
private final Element element;
public JMIElementListener(ElementNode referent) {
super(referent, Utilities.activeReferenceQueue());
this.element = referent.element;
}
public void change(MDRChangeEvent e) {
final ElementNode n = (ElementNode) get();
if (n == null) return;
if (!e.isOfType(AttributeEvent.EVENTMASK_ATTRIBUTE)) return;
final AttributeEvent ae = (AttributeEvent) e;
String attrName = ae.getAttributeName();
try {
JavaMetamodel.getDefaultRepository().beginTrans(false);
ChangeDescriptor cd;
try {
cd = n.handleAttributeChange(ae);
} finally {
JavaMetamodel.getDefaultRepository().endTrans(false);
}
assert cd != null;
fireChangesInAWTThread(n, cd);
fireRegisteredProperties(attrName);
} catch (JmiException ex) {
ErrorManager.getDefault().notify(ErrorManager.WARNING, ex);
}
}
public void run() {
((MDRChangeSource) this.element).removeListener(this);
}
private void fireRegisteredProperties(String name) {
final ElementNode n = (ElementNode) get();
if (n == null) return;
if (name == null || (name = (String) n.getAttributeNameMap().get(name)) != null) {
n.superPropertyChange(name, null, null);
}
}
}
final static class ChangeDescriptor {
String displayName;
String name;
String iconBase;
String shortDescription;
/** recreate property sheet */
Sheet sheet;
public ChangeDescriptor(String displayName, String iconBase, String name, String shortDescription) {
this.displayName = displayName;
this.iconBase = iconBase;
this.name = name;
this.shortDescription = shortDescription;
this.sheet = null;
}
public ChangeDescriptor() {
}
}
// ================== Property support for element nodes =================
/** Property support for element nodes properties.
*/
static abstract class ElementProp extends PropertySupport {
/** caches a reference to the property editor */
private Reference editor = null;
/** Constructs a new ElementProp - support for properties of
* element hierarchy nodes.
*
* @param name The name of the property
* @param type The class type of the property
* @param canW The canWrite flag of the property
*/
public ElementProp(String name, java.lang.Class type, boolean canW) {
super(name, type,
getString("PROP_" + name), // NOI18N
getString("HINT_" + name), // NOI18N
true, canW);
}
/** Setter for the value. This implementation only tests
* if the setting is possible.
*
* @param val the value of the property
* @exception java.lang.IllegalAccessException when this ElementProp was constructed
* like read-only.
*/
public void setValue (Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
if (!canWrite())
throw new IllegalAccessException(getString("MSG_Cannot_Write")); // NOI18N
}
public final PropertyEditor getPropertyEditor() {
PropertyEditor pe;
if (editor == null || (pe = (PropertyEditor) editor.get()) == null) {
pe = createPropertyEditor();
editor = new SoftReference(pe);
}
return pe;
}
/**
* override just to provide own property editor.
* @return own property editor
*/
protected PropertyEditor createPropertyEditor() {
return super.getPropertyEditor();
}
}
/** creates a read-only node property for class member name.
* @param element class memeber
* @return the property
*/
public static Node.Property createNameProperty(NamedElement element) {
Node.Property prop = new NameProperty(element);
return prop;
}
/** creates a node property for constructor or method parameters.
* @param element element owning parameters
* @param canW false to force property to be read-only
* @return the property
*/
public static Node.Property createParametersProperty(CallableFeature element, boolean canW) {
Node.Property prop = new ParametersProperty(element, canW);
setModel(element, prop);
return prop;
}
/** creates a node property for constructor or method exceptions.
* @param element element owning exceptions
* @param canW false to force property to be read-only
* @return the property
*/
public static Node.Property createExceptionsProperty(CallableFeature element, boolean canW) {
Node.Property prop = new ExceptionsProperty(element, canW);
setModel(element, prop);
return prop;
}
/** creates a node property for field type or method return type.
* @param name property name
* @param element element
* @param canW false to force property to be read-only
* @return the property
*/
public static Node.Property createTypeProperty(String name, TypedElement element, boolean canW) {
Node.Property prop = new TypeProperty(name, element, canW);
setModel(element, prop);
return prop;
}
/** creates a node property for generic type type.
* @param name property name
* @param element element
* @param canW false to force property to be read-only
* @return the property
*/
public static Node.Property createTypeParametersProperty(String name, GenericElement element, boolean canW) {
Node.Property prop = new TypeParametersProperty(name, element, canW);
setModel(element, prop);
return prop;
}
/** creates a node property for element modifiers.
* @param element element owning modifiers
* @param canW false to force property to be read-only
* @return the property
*/
public static Node.Property createModifiersProperty(ClassMember element, boolean canW) {
Node.Property prop = new ModifiersProperty(element, canW);
setModel(element, prop);
return prop;
}
/** @see #getModel */
public static void setModel(org.netbeans.jmi.javamodel.Element el, FeatureDescriptor fd) {
JavaModelPackage model = JavaMetamodel.getManager().getJavaExtent(el);
fd.setValue("JavaModelPackage", model); // NOI18N
}
/** extracts model from descriptor or provides default one if custom not exists*/
public static JavaModelPackage getModel(FeatureDescriptor fd) {
JavaModelPackage model = (JavaModelPackage) fd.getValue("JavaModelPackage"); // NOI18N
if (model == null) {
model = JavaMetamodel.getManager().getDefaultExtent();
ErrorManager.getDefault().notify(
ErrorManager.INFORMATIONAL,
new IllegalStateException("missing JavaModelPackage")); // NOI18N
}
return model;
}
/** Create a node property for the modifiers of the element.
* This property will typically display with a custom editor
* allowing individual modifiers to be examined.
* @param canW if false , the property will be read-only irrespective of
* the underlying element's ability to change the modifiers
* @return the property
*/
protected Node.Property createModifiersProperty(boolean canW) {
Node.Property p = createModifiersProperty((ClassMember) element, canW);
p.setValue("changeImmediate" /* PropertyEnv.PROP_CHANGE_IMMEDIATE */,Boolean.FALSE); // NOI18N
return p;
}
/** Options for the display name format. */
protected static SourceOptions getSourceOptions() {
return (SourceOptions) SharedClassObject.findObject(SourceOptions.class, true);
}
private final static class ParametersProperty extends ElementProp {
private final CallableFeature element;
private ParametersProperty(CallableFeature element, boolean canW) {
super(PROP_PARAMETERS, Parameter[].class, canW);
this.element = element;
}
public Object getValue() {
List l = element.getParameters();
return (Parameter[]) l.toArray(new Parameter[l.size()]);
}
public PropertyEditor createPropertyEditor() {
return new MethodParameterArrayEditor();
}
public void setValue(final Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
super.setValue(val);
if (!(val instanceof Parameter[]))
throw new IllegalArgumentException();
boolean fail = true;
try {
JavaMetamodel.getDefaultRepository().beginTrans(true);
try {
List l = element.getParameters();
l.clear();
l.addAll(Arrays.asList((Parameter[]) val));
fail = false;
} finally {
JavaMetamodel.getDefaultRepository().endTrans(fail);
}
} catch (JmiException e) {
IllegalArgumentException iae = new IllegalArgumentException();
iae.initCause(e);
throw iae;
}
}
}
private final static class ExceptionsProperty extends ElementProp {
private final CallableFeature element;
public ExceptionsProperty(CallableFeature element, boolean canW) {
super(PROP_EXCEPTIONS, MultipartId[].class, canW);
this.element = element;
}
protected PropertyEditor createPropertyEditor() {
return new IdentifierArrayEditor();
}
public Object getValue () {
Object ret = null;
try {
JavaMetamodel.getDefaultRepository().beginTrans(false);
try {
List l = element.getExceptionNames();
ret = l.toArray(new MultipartId[l.size()]);
} finally {
JavaMetamodel.getDefaultRepository().endTrans();
}
} catch (JmiException e) {
ErrorManager.getDefault().notify(e);
}
return ret;
}
public void setValue(final Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
super.setValue(val);
if (!(val instanceof MultipartId[]))
throw new IllegalArgumentException();
boolean fail = true;
try {
JavaMetamodel.getDefaultRepository().beginTrans(true);
try {
List exs = element.getExceptionNames();
exs.clear();
exs.addAll(Arrays.asList((MultipartId[]) val));
fail = false;
} finally {
JavaMetamodel.getDefaultRepository().endTrans(fail);
}
} catch (JmiException e) {
IllegalArgumentException iae = new IllegalArgumentException();
iae.initCause(e);
throw iae;
}
}
}
private final static class TypeProperty extends ElementProp {
private final TypedElement element;
public TypeProperty(String propName, TypedElement element, boolean canW) {
super(propName, Type.class, canW);
this.element = element;
}
public PropertyEditor createPropertyEditor() {
return new TypeEditor();
}
public Object getValue () {
return element.getType();
}
public void setValue(Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
super.setValue(val);
if (!(val instanceof Type))
throw new IllegalArgumentException();
Type type = (Type) val;
boolean fail = true;
try {
JavaMetamodel.getDefaultRepository().beginTrans(true);
try {
element.setType(type);
fail = false;
} finally {
JavaMetamodel.getDefaultRepository().endTrans(fail);
}
} catch (JmiException e) {
IllegalArgumentException iae = new IllegalArgumentException();
iae.initCause(e);
ErrorManager.getDefault().annotate(iae, ErrorManager.USER, null,
NbBundle.getMessage(FieldNode.class, "MSG_InvalidTypeDecl"), null, null); // NOI18N
throw iae;
}
}
}
private final static class TypeParametersProperty extends ElementProp {
private final GenericElement element;
public TypeParametersProperty(String name, GenericElement element, boolean canW) {
super(name, TypeParameter[].class, false);
this.element = element;
}
protected PropertyEditor createPropertyEditor() {
return new TypeParameterArrayEditor();
}
public Object getValue() throws IllegalAccessException, InvocationTargetException {
return element.getTypeParameters().toArray(new TypeParameter[0]);
}
public void setValue(Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
throw new InvocationTargetException(new UnsupportedOperationException());
}
}
private final static class ModifiersProperty extends ElementProp {
private final ClassMember element;
private ModifiersProperty(ClassMember element, boolean canW) {
super(PROP_MODIFIERS, Integer.class, canW);
this.element = element;
}
/** Gets the value */
public Object getValue () {
return new Integer(element.getModifiers());
}
/** Sets the value */
public void setValue(final Object val) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
super.setValue(val);
if (!(val instanceof Integer))
throw new IllegalArgumentException();
element.setModifiers(((Integer) val).intValue());
}
/** Define property editor for this property. */
public PropertyEditor createPropertyEditor () {
// XXX see http://www.netbeans.org/issues/show_bug.cgi?id=42155
return new ModifierEditor(SourceEditSupport.getModifiersMask(this.element));
}
}
private static final class NameProperty extends ElementProp {
private final NamedElement element;
public NameProperty(NamedElement element) {
super(ElementProperties.PROP_NAME, String.class, false);
this.element = element;
}
public Object getValue () {
return element.getName();
}
}
private static final class OpenCookieImpl implements OpenCookie {
private final ElementNode node;
public OpenCookieImpl(ElementNode node) {
this.node = node;
}
public void open() {
try {
DataObject d = node.getDataObject();
PositionBounds bounds = JavaMetamodel.getManager().getElementPosition(node.element);
if (bounds == null)
return;
((JavaEditor) d.getCookie(JavaEditor.class)).openAtPosition(bounds.getBegin());
} catch (javax.jmi.reflect.InvalidObjectException e) {
}
}
}
}
|
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.