Constructing Nodes from the Layer and from a Database

Now here's a pretty cool piece of code.

Let's imagine you're populating a node hierarchy from a database. In addition, you want to populate the same node hierarchy from the filesystem because you're using @ConvertAsJavaBean to serialize a new business object whenever the user steps through a wizard to create a new customer. At that point, i.e., when Finish is clicked, the .settings file is created in the userdir because maybe the user doesn't want to save the new object as soon as it is created.

So now you need a way to visually distinguish between nodes built from objects in the database versus nodes built from objects in the layer.

Here's how, thanks to Ernest from the course in Stellenbosch. (As the trainer, I set the task but couldn't completely figure out the answer myself.) Look at the code below. We listen for new objects in the layer, at which point we refresh the node hierarchy, i.e., refresh calls createKeys. In createNodeForKey, a FilterNode is used to add an asterisk to the display name of the node, if the underlying object is found in the result object (i.e., in that case, it doesn't come from the database):

public class BusAcctChildFactory extends ChildFactory< BusAcct>  implements LookupListener {

    private final Result< BusAcct>  result;
    private InstanceContent ic;

    public BusAcctChildFactory() {
        //Here we're listening to a folder in the layer
        //for new .settings files, which are
        //serialized from 'BusAcct' business object,
        //via @ConvertAsJavaBean:
        result = Lookups.forPath("BusinessAccounts").lookupResult(BusAcct.class);
        result.addLookupListener(this);
        resultChanged(new LookupEvent(result));
    }

    @Override
    public void resultChanged(LookupEvent ev) {
        refresh(true);
    }

    @Override
    protected boolean createKeys(List< BusAcct>  toPopulate) {

        //Populate the list from the db:
        EntityManager em = Persistence.createEntityManagerFactory("AMSWinJPU").
                createEntityManager();
        em.getTransaction().begin();
        Query query = em.createQuery("SELECT c FROM BusAcct c");
        List< BusAcct>  resultList = query.getResultList();
        for (BusAcct busAcct : resultList) {
            toPopulate.add(busAcct);
        }

        //Populate the list from the layer:
        Collection< ? extends BusAcct>  busAcctsFromFS = result.allInstances();
        for (BusAcct busAcct : busAcctsFromFS) {
            toPopulate.add(busAcct);
        }

        return true;

    }

    
    @Override
    protected Node createNodeForKey(BusAcct key) {
        //Create new InstanceContent per Node:
        ic = new InstanceContent();
        ic.add(key);
        BusAcctNode node = null;
        try {
            node = new BusAcctNode(ic);
            //if the key is from the layer,
            //which we can check by seeing
            //if the current object is in the result,
            //create a FilterNode and mark the
            //display name of the
            //object from the layer as not being
            //saved in the database:
            if (result.allInstances().contains(key)) {
                return new FilterNode(node) {
                    @Override
                    public String getDisplayName() {
                        return "\*" + super.getDisplayName();
                    }
                };
            }
        } catch (IntrospectionException ex) {
            Exceptions.printStackTrace(ex);
        }
        return node;
    }

}

An even better solution, also by Ernest. Create a property for the node, for the "dirty" asterisk:

private class DirtyProperty extends PropertySupport.ReadOnly<String>  {

    private final BusAcct c;

    private DirtyProperty(BusAcct c) {
        super("dirty", String.class, "Dirty", "Property marking as dirty");
        this.c = c;
    }

    @Override
    public String getValue() throws IllegalAccessException, InvocationTargetException {
        Collection<? extends BusAcct>  allBusAcctsInLayer = Lookups.forPath("BusinessAccounts").lookupAll(BusAcct.class);
        if (allBusAcctsInLayer.contains(c)) {
            return "\*";
        } else {
            return "";
        }
    }
    
}

Then define the OutlineView as follows, using the name "dirty" to link it to the property above:

OutlineView ov = new OutlineView();

ov.setPropertyColumns(new String[]{
            "dirty", "",
            "baAmsZo", "ZO#",
            "baCompany", "Company",
            "nameFirst", "First Name",
            "nameLast", "Last Name",});

add(ov, BorderLayout.CENTER);

A much cleaner solution, the previous code for checking whether the layer folder contains the current object can be removed. Now look at the OutlineView below, with the "dirty" asterisk shown in the first column (hidden node column!) only for those objects that come from the layer:

Thanks Ernest! We'll be getting more tips and tricks from him on NetBeans Zone soon.

Comments:

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
20
21
22
23
24
25
26
27
28
29
30
   
       
Today