alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

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.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.
* 

Element nodes generally: *

    *
  • Have an associated icon, according to {@link #resolveIconBase}. *
  • Have a display name based on the element's properties, using {@link #elementFormat}; * changes to {@link org.openide.src.ElementFormat#dependsOnProperty relevant} element properties * automatically affect the display name. *
  • Have some node properties (displayable on the property sheet), according to * the element's properties, and with suitable editors. *
  • Permit renames and deletes, if a member element and writeable. *
  • As permitted by the element, and a writable flag in the node, * permit cut/copy/paste operations, as well as creation of new members. *
* * @author Petr Hamernik, Jan Pokorsky */ public abstract class ElementNode extends AbstractNode implements IconStrings, ElementProperties2, JMIElementCookie { private static ElementFormat invalidFormat; /** Default return value of getIconAffectingProperties method. */ private static final String[] ICON_AFFECTING_PROPERTIES = new String[] { PROP_MODIFIERS }; /** Associated element. */ protected Element element; /** Format for {@link java.beans.FeatureDescriptor#getDisplayName}. */ protected ElementFormat elementFormat = new ElementFormat (""); // NOI18N /** Is this node read-only or are modifications permitted? */ protected boolean writeable; private SystemAction preferredAction; private JMIElementListener wElementL; private PropertyChangeListener formatListener; /** Create a new element node. * * @param element element to represent * @param children child nodes * @param writeable 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

 

new blog posts

 

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.