#20842: partial rewrite of leaf & container node templates to QBE200301150100-BLD200301192142
authorjglick@netbeans.org
Sat, 11 Jan 2003 03:19:29 +0000
changeset 116731b82db83c95
parent 1166 d3a8a18849f1
child 1168 9f2ef1a69541
#20842: partial rewrite of leaf & container node templates to
emphasize that they should be driven entirely from an underlying data
model.
apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Children_java
apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Node_java
apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_leaf__Node_java
     1.1 --- a/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Children_java	Sat Jan 11 01:38:19 2003 +0000
     1.2 +++ b/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Children_java	Sat Jan 11 03:19:29 2003 +0000
     1.3 @@ -4,61 +4,50 @@
     1.4  
     1.5  import org.openide.nodes.*;
     1.6  
     1.7 -/** List of children of a containing node.
     1.8 +/**
     1.9 + * List of children of a containing node.
    1.10 + * Each child node is represented by one key from some data model.
    1.11   * Remember to document what your permitted keys are!
    1.12 - *
    1.13 + * Edit this template to work with the classes and logic of your data model.
    1.14   * @author __USER__
    1.15   */
    1.16 -public class __Sample_container__Children extends Children.Keys {
    1.17 +public class __Sample_container__Children extends Children.Keys implements MyDataListener {
    1.18  
    1.19 -    /** Optional holder for the keys, to be used when changing them dynamically. */
    1.20 -    protected List myKeys;
    1.21 +    private final MyDataModel model;
    1.22  
    1.23 -    public __Sample_container__Children() {
    1.24 -        myKeys = null;
    1.25 +    public __Sample_container__Children(MyDataModel model) {
    1.26 +        this.model = model;
    1.27      }
    1.28  
    1.29      protected void addNotify() {
    1.30          super.addNotify();
    1.31 -        if (myKeys != null) return;
    1.32 -        myKeys = new LinkedList();
    1.33 -        // add whatever keys you need
    1.34 -        setKeys(myKeys);
    1.35 +        // set the children to use:
    1.36 +        updateKeys();
    1.37 +        // and listen to changes in the model too:
    1.38 +        model.addModelListener(this);
    1.39 +    }
    1.40 +
    1.41 +    private void updateKeys() {
    1.42 +        // get your keys somehow from the data model:
    1.43 +        MyDataElement[] keys = model.getChildren();
    1.44 +        // you can also use Collection rather than an array
    1.45 +        setKeys(keys);
    1.46      }
    1.47  
    1.48      protected void removeNotify() {
    1.49 -        myKeys = null;
    1.50 +        model.removeModelListener(this);
    1.51          setKeys(Collections.EMPTY_SET);
    1.52          super.removeNotify();
    1.53      }
    1.54  
    1.55      protected Node[] createNodes(Object key) {
    1.56          // interpret your key here...usually one node generated, but could be zero or more
    1.57 -        return new Node[] {new MyNode((MyParameter)key)};
    1.58 +        return new Node[] {new MyNode((MyDataElement)key)};
    1.59      }
    1.60  
    1.61 -    /** Optional accessor method for the keys, for use by the container node or maybe subclasses. */
    1.62 -    /*
    1.63 -    protected addKey(Object newKey) {
    1.64 -	// Make sure some keys already exist:
    1.65 -	addNotify();
    1.66 -	myKeys.add(newKey);
    1.67 -	// Ensure that the node(s) is displayed:
    1.68 -	refreshKey(newKey);
    1.69 +    public void modelChanged(MyModelEvent ev) {
    1.70 +        // your data model changed, so update the children to match:
    1.71 +        updateKeys();
    1.72      }
    1.73 -    */
    1.74 -
    1.75 -    /** Optional accessor method for keys, for use by the container node or maybe subclasses. */
    1.76 -    /*
    1.77 -    protected void setKeys(Collection keys) {
    1.78 -	myKeys = new LinkedList();
    1.79 -	myKeys.addAll(keys);
    1.80 -	super.setKeys(keys);
    1.81 -    }
    1.82 -    */
    1.83 -
    1.84 -    // Could also write e.g. removeKey to be used by the nodes in this children.
    1.85 -    // Or, could listen to changes in their status (NodeAdapter.nodeDestroyed)
    1.86 -    // and automatically remove them from the keys list here. Etc.
    1.87  
    1.88  }
     2.1 --- a/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Node_java	Sat Jan 11 01:38:19 2003 +0000
     2.2 +++ b/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_container__Node_java	Sat Jan 11 03:19:29 2003 +0000
     2.3 @@ -6,14 +6,19 @@
     2.4  import org.openide.util.NbBundle;
     2.5  import org.openide.util.actions.SystemAction;
     2.6  
     2.7 -/** A node with some children.
     2.8 - *
     2.9 +/**
    2.10 + * A node with some children.
    2.11 + * The children are controlled by some underlying data model.
    2.12 + * Edit this template to work with the classes and logic of your data model.
    2.13   * @author __USER__
    2.14   */
    2.15  public class __Sample_container__Node extends AbstractNode {
    2.16  
    2.17 -    public __Sample_container__Node() {
    2.18 -        super(new __NAME$Node$Children$MyChildren__());
    2.19 +    private final MyDataModel model;
    2.20 +
    2.21 +    public __Sample_container__Node(MyDataModel model) {
    2.22 +        super(new __NAME$Node$Children$MyChildren__(model));
    2.23 +        this.model = model;
    2.24          setIconBase("__PACKAGE_AND_NAME_SLASHES$Node$NodeIcon$MyIcon__");
    2.25          // Whatever is most relevant to a user:
    2.26          setDefaultAction(SystemAction.get(PropertiesAction.class));
    2.27 @@ -26,10 +31,11 @@
    2.28          getCookieSet().add(new OpenCookie() {
    2.29  		public void open() {
    2.30  		    // Open something useful...
    2.31 +                    // typically using the data model.
    2.32  		}
    2.33  	    });
    2.34          */
    2.35 -        // Make reorderable:
    2.36 +        // Make reorderable (typically will pass in the data model):
    2.37          // getCookieSet().add(new ReorderMe());
    2.38      }
    2.39  
    2.40 @@ -65,15 +71,11 @@
    2.41          // return new HelpCtx(__NAME__.class);
    2.42      }
    2.43  
    2.44 -    protected __NAME$Node$Children$MyChildren__ get__NAME$Node$Children$MyChildren__() {
    2.45 -        return (__NAME$Node$Children$MyChildren__)getChildren();
    2.46 -    }
    2.47 -
    2.48      // RECOMMENDED - handle cloning specially (so as not to invoke the overhead of FilterNode):
    2.49      /*
    2.50      public Node cloneNode() {
    2.51  	// Try to pass in similar constructor params to what you originally got:
    2.52 -	return new __NAME__();
    2.53 +	return new __NAME__(model);
    2.54      }
    2.55      */
    2.56  
    2.57 @@ -84,15 +86,16 @@
    2.58      // from the root node, traversed by Node.name.
    2.59      /*
    2.60      public Node.Handle getHandle() {
    2.61 -        return new __NAME$Node$Handle$NodeHandle__();
    2.62 +        return new __NAME$Node$Handle$NodeHandle__(model);
    2.63      }
    2.64      private static final class __NAME$Node$Handle$NodeHandle__ implements Node.Handle {
    2.65          private static final long serialVersionUID = 1L;
    2.66 -        public __NAME$Node$Handle$NodeHandle__() {
    2.67 -            // store any state for the node as nontransient instance fields...
    2.68 +        private MyDataModel model; // must be serializable for this to work of course!
    2.69 +        public __NAME$Node$Handle$NodeHandle__(MyDataModel model) {
    2.70 +            this.model = model;
    2.71          }
    2.72          public Node getNode() throws IOException {
    2.73 -            return new __NAME__();
    2.74 +            return new __NAME__(model);
    2.75          }
    2.76      }
    2.77      */
    2.78 @@ -107,6 +110,7 @@
    2.79  	    props = Sheet.createPropertiesSet();
    2.80  	    sheet.put(props);
    2.81  	}
    2.82 +        // Typically will pass the model to the property classes:
    2.83  	props.put(new MyProp(someParams));
    2.84          return sheet;
    2.85      }
    2.86 @@ -124,10 +128,11 @@
    2.87  		//     return __NAME__.class.getName() + ".newType";
    2.88  		// }
    2.89  		public void create() throws IOException {
    2.90 -		    // do whatever you need, e.g.:
    2.91 -		    get__NAME$Node$Children$MyChildren__().addKey(someNewKey);
    2.92 -		    // Throw an IOException if you are creating an underlying
    2.93 -		    // object and this fails.
    2.94 +                    // Normally implemented by prompting the user with a dialog
    2.95 +                    // for some information; then creating and adding a new element
    2.96 +                    // with those parameters to the data model. This should cause the
    2.97 +                    // children to update automatically because of its listener.
    2.98 +                    // throw IOException if the data model operation fails.
    2.99  		}
   2.100  	    }
   2.101  	};
   2.102 @@ -155,10 +160,8 @@
   2.103  			    // Node n = NodeTransfer.node(t, NodeTransfer.COPY);
   2.104  			    // Node[] ns = NodeTransfer.nodes(t, NodeTransfer.MOVE);
   2.105  			    // MyCookie cookie = (MyCookie)NodeTransfer.cookie(t, NodeTransfer.COPY, MyCookie.class);
   2.106 -			    // do something, e.g.:
   2.107 -			    get__NAME$Node$Children$MyChildren__().addKey(data);
   2.108 -			    // Throw an IOException if you are creating an underlying
   2.109 -			    // object and this fails.
   2.110 +			    // do something, typically involving the data model...
   2.111 +                            // throw IOException if the data model operation fails.
   2.112  			    // To leave the clipboard as is:
   2.113  			    return null;
   2.114  			    // To clear the clipboard:
   2.115 @@ -179,9 +182,18 @@
   2.116  	return true;
   2.117      }
   2.118      public void setName(String nue) {
   2.119 -	// Update visible name, fire property changes:
   2.120 -	super.setName(nue);
   2.121 -	// perform additional actions, i.e. rename underlying object
   2.122 +        // Typically implemented by changing the name of an element from an underlying
   2.123 +        // data model. This node class should be listening to changes in the name of the
   2.124 +        // element and calling super.setName when it notices any (or better, override getName
   2.125 +        // and perhaps getDisplayName and call fireNameChange).
   2.126 +        // For example, you might write this method as:
   2.127 +        // model.setName(nue);
   2.128 +        // where you would also have:
   2.129 +        // public String getName() {return model.getName();}
   2.130 +        // and in the constructor, if __NAME__ implements ModelListener:
   2.131 +        // model.addModelListener((ModelListener)WeakListener.create(ModelListener.class, this, model));
   2.132 +        // where the interface is implemented as:
   2.133 +        // public void modelChanged(ModelEvent ev) {fireNameChange(null, null);}
   2.134      }
   2.135      */
   2.136  
   2.137 @@ -191,10 +203,12 @@
   2.138  	return true;
   2.139      }
   2.140      public void destroy() throws IOException {
   2.141 -	// Actually remove the node itself and fire property changes:
   2.142 -	super.destroy();
   2.143 -	// perform additional actions, i.e. delete underlying object
   2.144 -	// (and don't forget about objects represented by your children!)
   2.145 +        // Typically implemented by removing an element from an underlying data model.
   2.146 +        // For example, you might write this method as:
   2.147 +        // model.getParentModel().removeSubModel(model);
   2.148 +        // The parent container children should be listening to the model, notice
   2.149 +        // the removal, set a new key list without this data element, and thus
   2.150 +        // remove this node from its children list.
   2.151      }
   2.152      */
   2.153  
   2.154 @@ -211,6 +225,7 @@
   2.155  	ExTransferable et = ExTransferable.create(super.clipboardCopy());
   2.156  	et.put(new ExTransferable.Single(DataFlavor.stringFlavor) {
   2.157  		protected Object getData() {
   2.158 +                    // typically based on the data model, but for example:
   2.159  		    return __NAME__.this.getDisplayName();
   2.160  		}
   2.161  	    });
   2.162 @@ -224,6 +239,7 @@
   2.163  	// and it is not safe to assume that getData will only be called once):
   2.164  	et.put(new ExTransferable.Single(DataFlavor.stringFlavor) {
   2.165  		protected Object getData() {
   2.166 +                    // typically based on the data model, but for example:
   2.167  		    return __NAME__.this.getDisplayName();
   2.168  		}
   2.169  	    });
   2.170 @@ -237,6 +253,7 @@
   2.171  	return true;
   2.172      }
   2.173      public Component getCustomizer() {
   2.174 +        // will typically be passed the data model to customize:
   2.175  	return new MyCustomizingPanel(this);
   2.176      }
   2.177      */
   2.178 @@ -260,12 +277,15 @@
   2.179  	// of the Index cookie.
   2.180  	public void reorder(int[] perm) {
   2.181  	    // Remember: {2, 0, 1} cycles three items forwards.
   2.182 -	    List old = __NAME__.this.get__NAME$Node$Children$MyChildren__().myKeys;
   2.183 -	    if (list.size() != perm.length) throw new IllegalArgumentException();
   2.184 -	    List nue = new ArrayList(perm.length);
   2.185 -	    for (int i = 0; i < perm.length; i++)
   2.186 -		nue.set(i, old.get(perm[i]));
   2.187 -	    __NAME__.this.get__NAME$Node$Children$MyChildren__().setKeys(nue);
   2.188 +	    MyDataElement[] items = model.getChildElements();
   2.189 +	    if (items.length != perm.length) throw new IllegalArgumentException();
   2.190 +	    MyDataElement[] nue = new MyDataElement[perm.length];
   2.191 +	    for (int i = 0; i < perm.length; i++) {
   2.192 +		nue[i] = old[perm[i]];
   2.193 +            }
   2.194 +            // Should trigger an automatic child node update because the children
   2.195 +            // should be listening:
   2.196 +            model.setChildElements(nue);
   2.197  	}
   2.198        
   2.199      }
     3.1 --- a/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_leaf__Node_java	Sat Jan 11 01:38:19 2003 +0000
     3.2 +++ b/apisupport/src/org/netbeans/modules/apisupport/resources/templates/Nodes_API/__Sample_leaf__Node_java	Sat Jan 11 03:19:29 2003 +0000
     3.3 @@ -6,12 +6,17 @@
     3.4  import org.openide.util.NbBundle;
     3.5  import org.openide.util.actions.SystemAction;
     3.6  
     3.7 -/** A simple node with no children.
     3.8 - *
     3.9 +/**
    3.10 + * A simple node with no children.
    3.11 + * Often used in conjunction with some kind of underlying data model, where
    3.12 + * each node represents an element in that model. In this case, you should see
    3.13 + * the Container Node template which will permit you to create a whole tree of
    3.14 + * such nodes with the proper behavior.
    3.15   * @author __USER__
    3.16   */
    3.17  public class __Sample_leaf__Node extends AbstractNode {
    3.18  
    3.19 +    // will frequently accept an element from some data model in the constructor:
    3.20      public __Sample_leaf__Node() {
    3.21          super(Children.LEAF);
    3.22          setIconBase("__PACKAGE_AND_NAME_SLASHES$Node$NodeIcon$MyIcon__");
    3.23 @@ -26,9 +31,15 @@
    3.24          getCookieSet().add(new OpenCookie() {
    3.25  		public void open() {
    3.26  		    // Open something useful...
    3.27 +                    // will typically use the data model somehow
    3.28  		}
    3.29  	    });
    3.30          */
    3.31 +        // If this node represents an element in a data model of some sort, consider
    3.32 +        // creating your own cookie which captures the existence of that underlying data,
    3.33 +        // and add it to the cookie set. Then you can write actions sensitive to that cookie,
    3.34 +        // and they will not need to directly refer to this node class - only to the cookie
    3.35 +        // and the data model.
    3.36      }
    3.37  
    3.38      // Create the popup menu:
    3.39 @@ -59,7 +70,8 @@
    3.40      // RECOMMENDED - handle cloning specially (so as not to invoke the overhead of FilterNode):
    3.41      /*
    3.42      public Node cloneNode() {
    3.43 -	// Try to pass in similar constructor params to what you originally got:
    3.44 +	// Try to pass in similar constructor params to what you originally got,
    3.45 +        // typically meaning passing in the same data model element:
    3.46  	return new __NAME__();
    3.47      }
    3.48      */
    3.49 @@ -74,6 +86,7 @@
    3.50  	    props = Sheet.createPropertiesSet();
    3.51  	    sheet.put(props);
    3.52  	}
    3.53 +        // typically the property will be constructed based on some underlying data model:
    3.54  	props.put(new MyProp(someParams));
    3.55          return sheet;
    3.56      }
    3.57 @@ -85,9 +98,20 @@
    3.58  	return true;
    3.59      }
    3.60      public void setName(String nue) {
    3.61 -	// Update visible name, fire property changes:
    3.62 -	super.setName(nue);
    3.63 -	// perform additional actions, i.e. rename underlying object
    3.64 +        // Typically implemented by changing the name of an element from an underlying
    3.65 +        // data model. This node class should be listening to changes in the name of the
    3.66 +        // element and calling super.setName when it notices any (or better, override getName
    3.67 +        // and perhaps getDisplayName and call fireNameChange).
    3.68 +        // For example, if there is an instance field
    3.69 +        // private final MyDataElement data;
    3.70 +        // then you might write this method as:
    3.71 +        // data.setID(nue);
    3.72 +        // where you would also have:
    3.73 +        // public String getName() {return data.getID();}
    3.74 +        // and in the constructor, if __NAME__ implements ModelListener:
    3.75 +        // data.addModelListener((ModelListener)WeakListener.create(ModelListener.class, this, data));
    3.76 +        // where the interface is implemented as:
    3.77 +        // public void modelChanged(ModelEvent ev) {fireNameChange(null, null);}
    3.78      }
    3.79      */
    3.80  
    3.81 @@ -97,9 +121,14 @@
    3.82  	return true;
    3.83      }
    3.84      public void destroy() throws IOException {
    3.85 -	// Actually remove the node itself and fire property changes:
    3.86 -	super.destroy();
    3.87 -	// perform additional actions, i.e. delete underlying object
    3.88 +        // Typically implemented by removing an element from an underlying data model.
    3.89 +        // For example, if there is an instance field
    3.90 +        // private final MyDataElement data;
    3.91 +        // then you might write this method as:
    3.92 +        // data.getContainingModel().removeElement(data);
    3.93 +        // The parent container children should be listening to the model, notice
    3.94 +        // the removal, set a new key list without this data element, and thus
    3.95 +        // remove this node from its children list.
    3.96      }
    3.97      */
    3.98  
    3.99 @@ -116,7 +145,9 @@
   3.100  	ExTransferable et = ExTransferable.create(super.clipboardCopy());
   3.101  	et.put(new ExTransferable.Single(DataFlavor.stringFlavor) {
   3.102  		protected Object getData() {
   3.103 +                    // just an example:
   3.104  		    return __NAME__.this.getDisplayName();
   3.105 +                    // more commonly, will use some underlying data model
   3.106  		}
   3.107  	    });
   3.108  	return et;
   3.109 @@ -129,7 +160,9 @@
   3.110  	// and it is not safe to assume that getData will only be called once):
   3.111  	et.put(new ExTransferable.Single(DataFlavor.stringFlavor) {
   3.112  		protected Object getData() {
   3.113 +                    // just an example:
   3.114  		    return __NAME__.this.getDisplayName();
   3.115 +                    // more commonly, will use some underlying data model
   3.116  		}
   3.117  	    });
   3.118  	return et;
   3.119 @@ -142,6 +175,7 @@
   3.120  	return true;
   3.121      }
   3.122      public Component getCustomizer() {
   3.123 +        // more commonly, will pass in underlying data:
   3.124  	return new MyCustomizingPanel(this);
   3.125      }
   3.126      */