More on @ConvertAsJavaBean

Improved version of previous blog entry on the new @ConvertAsJavaBean annotation.

  1. Create a bean with PropertyChangeSupport, annotated with @ConvertAsJavaBean:
    @ConvertAsJavaBean()
    public class CountryBean {
    
        protected String name;
        protected String food;
        private PropertyChangeSupport propertyChangeSupport;
    
        public CountryBean() {
            propertyChangeSupport = new PropertyChangeSupport(this);
        }
    
        public String getFood() {
            return food;
        }
    
        public void setFood(String newValue) {
            String oldValue = this.food;
            this.food = newValue;
            propertyChangeSupport.firePropertyChange("food", oldValue, newValue);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String newValue) {
            String oldValue = this.name;
            this.name = newValue;
            propertyChangeSupport.firePropertyChange("name", oldValue, newValue);
        }
    
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            propertyChangeSupport.addPropertyChangeListener(listener);
        }
    
        public void removePropertyChangeListener(PropertyChangeListener listener) {
            propertyChangeSupport.removePropertyChangeListener(listener);
        }
    
    }

  2. Create a root Node with an action that creates a new ".settings" file in a "Countries" folder whenever a new bean is created. It also opens the Properties sheet so that the values can be customized immediately. Changes made in the Properties sheet will automatically be persisted to the ".settings" file because of the PropertyChangeSupport on the bean.
    class RootNode extends AbstractNode {
    
        public RootNode(Children kids) {
            super(kids);
            setDisplayName("Root");
        }
    
        @Override
        public Action[] getActions(boolean bln) {
            Action[] actions = new Action[]{
                new CreateNewCountryBeanAction()
            };
            return actions;
        }
    
        public class CreateNewCountryBeanAction extends AbstractAction {
            public CreateNewCountryBeanAction() {
                putValue(Action.NAME, "Create new country");
            }
            @Override
            public void actionPerformed(ActionEvent arg0) {
                CountryBean bean = new CountryBean();
                bean.setName("state");
                bean.setFood("meal");
                try {
                    NodeOperation.getDefault().showProperties(new BeanNode(bean));
                    InstanceDataObject ido = InstanceDataObject.create(
                        DataFolder.findFolder(FileUtil.getConfigFile("Countries")),
                        bean.getName(),
                        bean,
                        null, true
                    );
                } catch (Exception ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }
        
    }

  3. Then set that root Node as the root context of an ExplorerManager, together with this ChildFactory, which builds child nodes based on CountryBeans found in the "Countries" folder:
    public class CountryChildFactory extends ChildFactory<CountryBean> implements LookupListener {
    
        Result<CountryBean> res;
    
        public CountryChildFactory() {
            res = Lookups.forPath("Countries").lookupResult(CountryBean.class);
            res.addLookupListener(this);
            refresh(false);
        }
    
        @Override
        protected boolean createKeys(List<CountryBean> list) {
            list.addAll(res.allInstances());
            return true;
        }
    
        @Override
        protected Node createNodeForKey(final CountryBean countryBean) {
            BeanNode countryNode = null;
            try {
                countryNode = new BeanNode(countryBean);
            } catch (IntrospectionException ex) {
                Exceptions.printStackTrace(ex);
            }
            countryNode.setDisplayName(countryBean.getName());
            return countryNode;
        }
    
        @Override
        public void resultChanged(LookupEvent le) {
            refresh(false);
        }
        
    }

That's it. Thanks Jaroslav Tulach for this code.

Comments:

I don't believe you need to (or should) call refresh in the ChildFactory constructor; if and when the node is expanded, you will be asked for keys.

FindBugs would remind you to return null (or, better, rethrow an AssertionError) when catching IntrospectionException; otherwise you would just immediately get an NPE.

I believe BeanNode will automatically pick up its name from a getName method.

putValue(Action.NAME, ...) in an AbstractAction constructor can just be replaced with a call to the super constructor taking String.

Posted by Jesse Glick on September 14, 2009 at 01:22 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
21
22
23
24
25
26
27
28
29
30
   
       
Today