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

import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.FilterNode;
import org.openide.cookies.FilterCookie;
import org.openide.src.ElementProperties;
import org.openide.ErrorManager;
import org.netbeans.jmi.javamodel.*;
import org.netbeans.modules.java.ui.nodes.SourceNodeFactory;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
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.jmi.reflect.InvalidObjectException;

/** Normal implementation of children list for a class element node.
* @author Dafe Simonek, Jan Jancura, Jan Pokorsky
*/
public class ClassChildren extends Children.Keys implements FilterCookie, ChildrenProvider.KeyHandler {

    /** Support for PACKAGE modifier */
    private static int PPP_MASK = SourceElementFilter.PUBLIC + SourceElementFilter.PRIVATE +  SourceElementFilter.PROTECTED;
    /** Converts property names to filter.
     * XXX seems to be obsolete and can be removed; investigate Categories.FILTER_CATEGORIES 
     */
    protected static HashMap propToFilter;

    /** For sorting groups of elements. */
    private static Comparator comparator = new Comparator() {
                public int compare(Object o1, Object o2) {
                    if (o1 instanceof NamedElement)
                        if (o2 instanceof NamedElement) {
                            String o1Name = getName((NamedElement) o1);
                            String o2Name = getName((NamedElement) o2);
                            if (o1Name==null || o2Name==null) return 1; 
                            return o1Name.compareToIgnoreCase(o2Name);
                        } else
                            return -1;
                    else
                        if (o2 instanceof NamedElement)
                            return 1;
                        else
                            return 0;
                }
        
                private String getName(NamedElement el) {
                    String name;
                    if (el instanceof Constructor) {
                        name = ((Constructor) el).getDeclaringClass().getName();
                    } else if (el instanceof Initializer) {
                        name = ""; // NOI18N
                    } else {
                        name = el.getName();
                    }
                    return name;
                }
            };

    static {
        propToFilter = new HashMap ();
        propToFilter.put (ElementProperties.PROP_CLASSES, new Integer (ClassElementFilter.CLASS | ClassElementFilter.INTERFACE));
        propToFilter.put (ElementProperties.PROP_METHODS, new Integer (ClassElementFilter.METHOD));
        propToFilter.put (ElementProperties.PROP_FIELDS, new Integer (ClassElementFilter.FIELD));
        propToFilter.put (ElementProperties.PROP_CONSTRUCTORS, new Integer (ClassElementFilter.CONSTRUCTOR));
        propToFilter.put (ElementProperties.PROP_INITIALIZERS, new Integer (ClassElementFilter.CONSTRUCTOR));
    }

    /** The class element whose subelements are represented. */
    protected ClassDefinition element;
    /** Filter for elements, or null to disable. */
    protected ClassElementFilter filter;
    /** Factory for creating new child nodes. */
    private SourceNodeFactory factory;
    /**Weak listener to the element and filter changes. This reference must
    * be kept to prevent the listener from finalizing when we are alive */
    private JMIElementListener wPropL;
    /** Flag saying whether we have our nodes initialized */
    private boolean nodesInited = false;
    private final ChildrenProvider chprovider = new ChildrenProvider(this);


    // init ................................................................................

    /** Create class children.
    * The children are initially unfiltered.
    * @param factory the factory to use to create new children
    * @param element attached class element (non-null)
    */
    public ClassChildren(SourceNodeFactory factory, ClassDefinition element) {
        if (factory == null) throw new NullPointerException("factory"); // NOI18N
        if (element == null) throw new NullPointerException("element"); // NOI18N
        this.element = element;
        this.factory = factory;
        this.filter = null;
    }

    protected SourceNodeFactory getFactory() {
        return this.factory;
    }

    /********** Implementation of filter cookie **********/

    /* @return The class of currently asociated filter or null
    * if no filter is asociated with these children.
    */
    public Class getFilterClass () {
        return ClassElementFilter.class;
    }

    /* @return The filter currently asociated with these children
    */
    public Object getFilter () {
        return filter;
    }

    /* Sets new filter for these children.
    * @param filter New filter. Null == disable filtering.
    */
    public void setFilter (final Object filter) {
        if (!(filter instanceof ClassElementFilter))
            throw new IllegalArgumentException();

        this.filter = (ClassElementFilter)filter;
        // change element nodes according to the new filter
        if (nodesInited)
            refreshAllKeys ();
    }


    // Children implementation ..............................................................

    /* Overrides initNodes to run the preparation task of the
    * source element, call refreshKeys and start to
    * listen to the changes in the element too. */
    protected void addNotify () {
        super.addNotify();
        // listen to the changes in the class element
        if (wPropL == null) {
            wPropL = new JMIElementListener(this);
        }
        refreshAllKeys ();
        ((MDRChangeSource) element).addListener(wPropL);
        nodesInited = true;
    }

    protected void removeNotify () {
        chprovider.clear();
        nodesInited = false;
        super.removeNotify();
    }
    
    protected final void hookNodeName(Element el) {
        if (el instanceof MDRChangeSource) {
            ((MDRChangeSource) el).addListener(wPropL);
        }
    }

    /* Creates node for given key.
    * The node is created using node factory.
    */
    protected final Node[] createNodes(Object key) {
        Node[] nodes;
        if (key instanceof Node) {
            nodes = new Node[] {new FilterNode((Node) key)};
        } else if (key instanceof Node[]) {
            Node[] ns = (Node[]) key;
            nodes = new Node[ns.length];
            for (int i = 0; i < ns.length; i++) {
                Node orig = ns[i];
                nodes[i] = orig == null? orig: new FilterNode(orig);
            }
        } else {
            nodes = new Node[] {factory.createErrorNode()};
            ErrorManager.getDefault().notify(
                    ErrorManager.WARNING,
                    new IllegalStateException("key: " + key) // NOI18N
            );
        }
        return nodes; 
    }
    
    protected Node[] createNodesImpl(Object key) throws JmiException {
        Node n;
        if (key instanceof Method) {
            Method m = (Method) key;
            hookNodeName(m);
            n = factory.createMethodNode(m);
        } else if (key instanceof Field) {
            Field f = (Field) key;
            hookNodeName(f);
            n = factory.createFieldNode(f);
        } else if (key instanceof Constructor) {
            // does not change its name; it is always null
            n = factory.createConstructorNode((Constructor) key);
        } else if (key instanceof JavaEnum) {
            JavaEnum en = (JavaEnum) key;
            hookNodeName(en);
            n = factory.createEnumNode(en);
        } else if (key instanceof AnnotationType) {
            AnnotationType at = (AnnotationType) key;
            hookNodeName(at);
            n = factory.createAnnotationTypeNode(at);
        } else if (key instanceof JavaClass) {
            JavaClass jc = (JavaClass) key;
            hookNodeName(jc);
            n = factory.createClassNode(jc);
        } else if (key instanceof Initializer) {
            // does not change its name; it is always null
            n = factory.createInitializerNode((Initializer) key);
        } else {
            // ?? unknown type
            n = factory.createErrorNode();
        }
        return new Node[] {n};
    }

    public final List collectKeys() {
        List keys = new LinkedList();
        int[] order = getOrder ();
        try {
            JavaMetamodel.getDefaultRepository().beginTrans(false);
            try {
                if (!element.isValid()) {
                    return keys;
                }
                List features = element.getFeatures();
        
                List members = new ArrayList(features.size());
                filterModifiers(features, members);
                
                for (int i = 0; i < order.length; i++) {
                    List keysOfType = getKeysOfType(members, order[i]);
                    if ((this.filter == null) || this.filter.isSorted ()) {
                        Collections.sort(keysOfType, comparator);
                    }
                    keys.addAll(keysOfType);
                }
            } finally {
                JavaMetamodel.getDefaultRepository().endTrans();
            }
        } catch (InvalidObjectException ex) {
            // some element is invalid. MDR will notify listeners about that change later
            keys = Collections.EMPTY_LIST;
        } catch (JmiException ex) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, ex);
        }
        return keys;
    }

    public Node[] prepareNodes(Object key) {
        return createNodesImpl(key);
    }

    public void presentKeys(List/**/ keys, List/**/ nodes) {
        JMIElementListener l = wPropL;
        if (l != null)
            l.updateElements(keys);
        setKeys(nodes);
    }

    public Node[] getNodes(boolean optimalResult) {
        if (!optimalResult) {
            return getNodes();
        }
        chprovider.waitFinished();
        return getNodes();
    }

    public Node findChild(String name) {
        Node n = super.findChild(name);
        if (n == null) {
            chprovider.waitFinished();
            n = super.findChild(name);
        }
        return n;
    }
    
    /************** utility methods ************/

    /** Updates all the keys (elements) according to the current filter &
    * ordering.
    */
    protected void refreshAllKeys() {
        refreshKeys(ClassElementFilter.ALL);
    }

    /** Updates all the keys with given filter.
    */
    protected final void refreshKeys(int filter) {
        if (!doRefresh(filter)) return;
        chprovider.recomputeChildren();
    }
    
    private boolean doRefresh(int filter) {
        int[] order = getOrder();
        for (int i = order.length - 1; i >= 0 ; i--) {
            if ((order[i] & filter) != 0) {
                return true;
            }
        }
        return false;
    }
    
    /** Filters and returns the keys of specified type.
    */
    protected List getKeysOfType(Collection/**/ elements, final int elementType) {
        List keys = new LinkedList();
        if ((elementType & ClassElementFilter.EXTENDS) != 0) {
            keys.add(element.getSuperClass());
        }
        if ((elementType & ClassElementFilter.IMPLEMENTS) != 0) {
            keys.addAll(element.getInterfaces());
        }
        
        Iterator it = elements.iterator();
        while (it.hasNext()) {
            Object member = it.next();
            if ((elementType & ClassElementFilter.FIELD) != 0 && member instanceof Field && !(member instanceof EnumConstant)) {
                keys.add(member);
            } else if ((elementType & ClassElementFilter.CONSTRUCTOR) != 0 &&
                    (member instanceof Constructor || member instanceof Initializer)) {
                keys.add(member);
            } else if ((elementType & ClassElementFilter.METHOD) != 0 && member instanceof Method) {
                keys.add(member);
            }
            if (member instanceof JavaClass) {
                boolean isInterface = ((JavaClass) member).isInterface();
                if ((elementType & ClassElementFilter.CLASS) != 0 && !isInterface) {
                    keys.add(member);
                } else if ((elementType & ClassElementFilter.INTERFACE) != 0 && isInterface) {
                    keys.add(member);
                }
            }
        }
        
        return keys;
    }

    /** Returns order form filter.
    */
    protected int[] getOrder () {
        return (filter == null || (filter.getOrder() == null))
               ? ClassElementFilter.DEFAULT_ORDER : filter.getOrder();
    }

    /** Returns modifier filter form filter.
    */
    private int getModifierFilter () {
        if (filter == null) return ClassElementFilter.ALL_MODIFIERS;
        return filter.getModifiers ();
    }

    /** Filters ClassMember for modifiers, and adds them to the given collection.
    */
    private void filterModifiers(Collection/**/ elements, Collection keys) {
        int ff = getModifierFilter();
        for (Iterator it = elements.iterator(); it.hasNext();) {
            ClassMember element = (ClassMember) it.next();
            int f = element.getModifiers();
            if ((f & PPP_MASK) == 0) f += ClassElementFilter.PACKAGE;
            if ((f & ff) != 0) keys.add(element);
            
        }
    }

    // innerclasses ...........................................................................

    /** The listener for listening to the property changes in the filter.
    */
    private static final class JMIElementListener extends java.lang.ref.WeakReference implements Runnable, MDRChangeListener {
        Collection elements;
        MDRChangeSource celem;

        JMIElementListener(ClassChildren cc) {
            super(cc, org.openide.util.Utilities.activeReferenceQueue());
            celem = (MDRChangeSource) cc.element;
        }

        ClassChildren getClassChildren() {
            Object o = get();
            return (ClassChildren) o;
        }


        public void change(MDRChangeEvent e) {
            int filter;
            final Object src = e.getSource();
            
            if ((src instanceof Element) && !((Element) src).isValid()) {
                return;
            }
            
            ClassChildren cc = getClassChildren();
//            System.out.println(">>> Children listener: " + propName + ", cc: " + cc);
            if (cc == null || !(e instanceof AttributeEvent))
                return;

            AttributeEvent evt = (AttributeEvent) e;
            String propName = evt.getAttributeName();
            
//            String snode = cc.getNode().getClass().getName() + "@" + System.identityHashCode(cc.getNode());
//            System.out.println("## MDRChangeEvent.node: " + snode);
//            System.out.println("## ...MDRChangeEvent: " + e);
//            if (e.isOfType(AttributeEvent.EVENTMASK_ATTRIBUTE)) {
//                AttributeEvent ae = (AttributeEvent) e;
//                System.out.println("  ## AttributeEvent.src: " + src.getClass().getName() + "@" + System.identityHashCode(this));
//                System.out.println("  ## AttributeEvent.name: " + ae.getAttributeName());
//                System.out.println("  ## AttributeEvent.old: " + ae.getOldElement());
//                System.out.println("  ## AttributeEvent.new: " + ae.getNewElement());
//            }
            
            if (src != cc.element) {
                if (src instanceof Element &&
                        (propName == null || ElementProperties.PROP_NAME == propName)) {
                    filter = chooseFilter((Element) src);
//                    System.out.println(">>> FILTER due to name");
                } else
                    return;
            } else if ("contents".equals(propName)) { // NOI18N
                Element cm = (Element) evt.getOldElement();
                cm = (cm == null)? (Element) evt.getNewElement(): cm;
                filter = chooseFilter(cm);
//                System.out.println(">>> FILTER due to content");
            } else if ("constants".equals(propName)) { // NOI18N
                filter = EnumFilter.CONSTANTS;
            } else {
                return;
//               Integer i = (Integer) cc.propToFilter.get(propName);
//               if (i == null)
//                   return;
//               filter = i.intValue();
            }
            if (cc.nodesInited) {
                cc.refreshKeys (filter);
            }
        }
        
        int chooseFilter(Element src) {
            int filter;
            if (src instanceof Method)
                filter = ClassElementFilter.METHOD;
            else if (src instanceof Constructor) 
                filter = ClassElementFilter.CONSTRUCTOR;
            else if (src instanceof Field)
                filter = ClassElementFilter.FIELD;
            else if (src instanceof EnumConstant)
                filter = EnumFilter.CONSTANTS;
            else
                filter = ClassElementFilter.CLASS | ClassElementFilter.INTERFACE | EnumFilter.ENUM;
            return filter;
        }

        // see also hookNodeName 
        void updateElements(Collection c) {
            Collection old = this.elements;
            if (old != null) {
                old.removeAll(c);
                removeListeners(old);
            }
            this.elements = c;
        }

        public void run() {
            // clean-up
            celem.removeListener(this);
            removeListeners(this.elements);
        }
        
        private void removeListeners(Collection c) {    
            for (Iterator it = c.iterator(); it.hasNext(); ) {
                Object o = it.next();
                if (!(o instanceof MDRChangeSource))
                    continue;
                MDRChangeSource el = (MDRChangeSource) o;
                el.removeListener(this);
            }
        }
    } // end of JMIElementListener inner class
    
}
... 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.