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;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.awt.*;
import javax.jmi.reflect.InvalidObjectException;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.jmi.reflect.JmiException;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.api.mdr.events.ExtentEvent;
import org.netbeans.api.mdr.events.MDRChangeEvent;
import org.netbeans.api.mdr.events.MDRChangeListener;
import org.netbeans.api.mdr.events.MDRChangeSource;

import org.openide.cookies.OpenCookie;
import org.openide.cookies.EditorCookie;

import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.ChoiceView;
import org.openide.explorer.view.Visualizer;
import org.openide.explorer.view.NodeListModel;

import org.openide.nodes.AbstractNode;
import org.openide.nodes.*;
import org.openide.util.Utilities;

import org.openide.windows.TopComponent;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;
import org.openide.ErrorManager;

import org.netbeans.modules.java.ui.nodes.elements.SourceChildren;
import org.netbeans.modules.java.ui.nodes.elements.SourceElementFilter;
import org.netbeans.modules.java.ui.nodes.SourceNodeFactory;
import org.netbeans.modules.java.ui.nodes.SourceNodes;
import org.netbeans.modules.java.ui.nodes.BridgeUtils;
import org.netbeans.modules.javacore.internalapi.JMIElementCookie;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.jmi.javamodel.*;

/**
 *
 * @author  sdedic
 * @version 
 */
public final class NavigationView extends ChoiceView {
    /**
     * Selection manager for keyboard events.
     */
    SelectionManager        selManager;
    
    /**
     * Listener that binds the View to the explorer manager and to the
     * TopComponent or whatever.
     */
    RootL                   rootChangeListener;
    
    /**
     * Cookie that provides data about the active source.
     */
    EditorCookie            sourceCookie;
    
    /**
     * Active Children object.
     */
    SourceChildren          sourceChildren;
    
    /**
     * Last observed root node - if changes, contents are reloaded completely.
     */
    Node                    rootNode;
    
    /**
     * Node factory specific for the data source.
     */
    SourceNodeFactory      nodeFactory;
    
    /**
     * Associated ExplorerManager.
     */
    ExplorerManager         manager;

    /**
     * TopComponent to listen to, or null if we should listen to
     * TopComponent.Registry.
     */
    TopComponent            topComponent;
    
    /**
     * Makes the Navigator ignore events from the combobox. This is used
     * during sync of the combo from the activated nodes.
     */
    boolean                 ignoreComboSelect;
    
    /**
     * Makes the Navigator ignore events from the activated nodes. This is
     * mainly used when the node is being "opened".
     */
    boolean                 ignoreActivatedNodes;
    
    boolean                 contentsChanging;
    
    DataObject              dataObject;
    
    private static final int MAX_NAVIGATOR_DEPTH = 4;
    
    static  java.util.ResourceBundle  bundle;
    
    /**
     * processor computing the navigation view content to not block
     * the awt event queue
     */ 
    private static final RequestProcessor RP = new RequestProcessor("Navigation View Queue"); // NOI18N
    /**
     * the last scheduled task
     * @see #RP
     */ 
    private WeakReference queuedTask;
    
    public NavigationView() {
        initComponents();
        MDRepository repo = JavaMetamodel.getDefaultRepository();
        ((MDRChangeSource)repo).addListener(new ExtentListener(this, repo));
    }

    static String getString(String key) {
        if (bundle == null)
            bundle = org.openide.util.NbBundle.getBundle(NavigationView.class);
        return bundle.getString(key);
    }

    private void initComponents() {
        nodeFactory = SourceNodes.getBrowserFactory();
        ((NodeListModel)getModel()).setDepth(MAX_NAVIGATOR_DEPTH);
        setKeySelectionManager(selManager = new SelectionManager());
        rootNode = new AbstractNode(Children.LEAF);
        this.getAccessibleContext().setAccessibleDescription(getString("ACSD_NavigationView"));

        // Anyone ever heard of this property ? Me not... until I looked through Swing sources.
        this.putClientProperty("JComboBox.lightweightKeyboardNavigation", "Lightweight"); // NOI18N
        
        // sets renderer that adds tooltip for nav view items that are too long
        // to fit in popup window (#33949)
        final ListCellRenderer renderer = this.getRenderer();
        this.setRenderer(new ListCellRenderer() {
            public Component getListCellRendererComponent(
                    JList list,
                    Object value,
                    int index,
                    boolean isSelected,
                    boolean cellHasFocus) {
                
                Component comp = renderer.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
                if (comp instanceof JLabel) {
                    JLabel jcomp = (JLabel) comp;
                    double listW = NavigationView.this.getWidth();
                    String txt = null;
                    if (listW > 0) {
                        double txtW = jcomp.getPreferredSize().getWidth();
                        if (txtW >= listW) {
                            txt = jcomp.getText();
                        }
                    }
                    
                    jcomp.setToolTipText(txt);
                }
                return comp;
            }
        });
    }

    SourceChildren createSourceChildren(Resource el) {
        dataObject = JavaMetamodel.getManager().getDataObject(el);
        SourceChildren c = new SourceChildren(nodeFactory, el);

        SourceElementFilter f = new SourceElementFilter();
        f.setAllClasses(true);
        c.setFilter(f);
        return c;
    }

    protected NodeListModel createModel () {
        return rootChangeListener = new RootL();
    }

    /**
     * Finds the context where the view is actually used.
     */
    public void addNotify() {
        super.addNotify();
    
        if (rootChangeListener == null)
            rootChangeListener = new RootL();
        
        topComponent = findParentTopComponent();

        getExplorerManager().setRootContext(rootNode);
        try {
            getExplorerManager().setSelectedNodes(new Node[] {});
        } catch (PropertyVetoException e) {
        }
        activatedNodeChanged(getActivatedNode());        
        if (topComponent == null) {
            TopComponent.getRegistry().addPropertyChangeListener(
                rootChangeListener);
        } else {
            topComponent.addPropertyChangeListener(rootChangeListener);
        }
        getExplorerManager().addPropertyChangeListener(rootChangeListener);
    }
    
    public void removeNotify() {
        if (topComponent != null)
            topComponent.removePropertyChangeListener(rootChangeListener);
        else
            TopComponent.getRegistry().removePropertyChangeListener(rootChangeListener);
        
        if (manager != null) {
            manager.removePropertyChangeListener(rootChangeListener);
        }
        super.removeNotify();
    }
        
    
    private ExplorerManager getExplorerManager() {
        if (manager == null) {
            manager = ExplorerManager.find(this);
        }
        return manager;
    }
    
    private TopComponent findParentTopComponent() {
        java.awt.Component p;
        
        for (p = getParent(); p != null && !(p instanceof TopComponent); p = p.getParent())
            ;
        return (TopComponent)p;
    }

    public void setPopupVisible(boolean show) {
        boolean wasVisible = isPopupVisible() && isVisible();
        super.setPopupVisible(show);
        if (!ignoreComboSelect && !show && wasVisible) {
            boolean saveActivatedNodes = ignoreActivatedNodes;
            try {
                ignoreActivatedNodes = true;
                choiceItemSelected(getExplorerManager().getSelectedNodes());
            } finally {
                ignoreActivatedNodes = saveActivatedNodes;
            }
        }
    }

    private Node getActivatedNode() {
        Node[] actNodes;
        
        if (topComponent == null) {
            actNodes = TopComponent.getRegistry().getActivatedNodes();
        } else {
            actNodes = topComponent.getActivatedNodes();
        }
        if (actNodes == null || actNodes.length == 0)
            return null;
        else 
            return actNodes[0];
    }
    
    private void activatedNodeChanged(Node newNode) {
        if (newNode == null) {
            return;
        }
        
        Node curNode = null;
        Node[] curNodes = getExplorerManager().getSelectedNodes();
        curNode = curNodes.length == 0 ? null : curNodes[0];
        if (curNode != null && curNode.equals(newNode)) {
            return;
        }
        selectNode(newNode, curNode);
    }

    private void selectNode(final Node newNode, final Node curNode) {
        // it is invoked just from the awt event thread so no special sync is necessary
        assert EventQueue.isDispatchThread();
        RequestProcessor.Task task = queuedTask == null ? null : (RequestProcessor.Task) queuedTask.get();
        if (task != null) {
            task.cancel();
        }
        task = RP.post(new Runnable() {
            public void run() {
                selectNodeImpl(newNode, curNode);
            }
        });
        queuedTask = new WeakReference(task);
    }
    
    private void selectNodeImpl(Node newNode, Node curNode) {
        Element el = null;
        ClassDefinition outer = null;
        Resource res = null;
        EditorCookie srcEditor = null;
        
        try {
            JavaMetamodel.getDefaultRepository().beginTrans(false);
            try {
                el = findElement(newNode);
                res = el != null? el.getResource(): null;
                DataObject d = res != null? JavaMetamodel.getManager().getDataObject(res): null;
                
                srcEditor = d!= null?
                                (EditorCookie) d.getCookie(EditorCookie.class):
                                null;
                if (el instanceof ClassMember) {
                    outer = ((ClassMember) el).getDeclaringClass();
                }
            } finally {
                JavaMetamodel.getDefaultRepository().endTrans();
            }
        } catch (InvalidObjectException e) {
            //do nothing - no need to select invalid objects
        } catch (JmiException e) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);            
        }

        if (srcEditor == null) {
            return;
        }
        
        if (srcEditor != sourceCookie) {
            changeRootContext(srcEditor, res);
        }

        // select the appropriate element from the list.
        ExplorerManager m = getExplorerManager();
        
        Node toSelect = findNode(m.getRootContext(), outer, el);

        if (toSelect == curNode || (curNode != null && curNode.equals(toSelect)))
            return;
        setSelectedNode(toSelect, m);
    }

    /**
     * finds node of target in rootNode's children
     * @param rootNode the root node
     * @param outer the class containing the target
     * @param target the element to search
     * @return node or null
     */ 
    private static Node findNode(Node rootNode, ClassDefinition outer, Element target) {
        if (outer != null && !(outer instanceof JavaClass)) {
            return null;
        }
        
        Node toSelect = null;
        Node[] classNodes = getSubNodes(rootNode);
        if (outer != null && !(target instanceof JavaClass)) {
            Node outerNode = findElementNode(classNodes, outer);
            if (outerNode != null) {
                classNodes = getSubNodes(outerNode);
                toSelect = findElementNode(classNodes, target); 
            }
        } else {
            toSelect = findElementNode(classNodes, target);
        }
        return toSelect;
    }

    private void setSelectedNode(final Node toSelect, final ExplorerManager m) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Node newNodes[];
            
                    if (toSelect == null)
                        newNodes = new Node[0];
                    else
                        newNodes = new Node[] { toSelect };
                    m.removePropertyChangeListener(rootChangeListener);
                    getExplorerManager().setSelectedNodes( newNodes );
                } catch (PropertyVetoException ex) {
                } finally {
                    m.addPropertyChangeListener(rootChangeListener);
                }
            }
        });
    }

    private void changeRootContext(EditorCookie cookie, Resource res) {
        // refill the combobox.
        sourceCookie = cookie;
        final Node n = new AbstractNode(sourceChildren = createSourceChildren(res));
        try {
            EventQueue.invokeAndWait(new Runnable() {
                public void run() {
                    Children c = new FChildren(n);
                    rootNode = new AbstractNode(c);
                    ((AbstractNode)rootNode).setIconBase("org/netbeans/modules/java/resources/class"); // NOI18N
                    rootNode.setDisplayName(getString("NAME_NothingSelected")); // NOI18N
                    manager.setRootContext(rootNode);
                }
            });
        } catch (InterruptedException e) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
        } catch (InvocationTargetException e) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e.getTargetException());
        }
    }
    
    private void checkRootContext() {
        if (dataObject == null) return;
        FChildren ch = (FChildren) rootNode.getChildren();
        ch.updateResource();
    }
    
    private void choiceItemSelected(Node[] sel) {
        if (sel == null || sel.length == 0)
            sel = getExplorerManager().getSelectedNodes();
        // Do nothing if nothing is selected (it is possible ?)
        if (sel == null || sel.length == 0)
            return;
        
        Node n = sel[0];

        OpenCookie o = (OpenCookie)n.getCookie(OpenCookie.class);
        if (o == null)
            return;
        
        // open the element / locate it in the editor.
        boolean saveActivatedNodes = ignoreActivatedNodes;
        try {
            ignoreActivatedNodes = true;
            o.open();
        } finally {
            ignoreActivatedNodes = saveActivatedNodes;
        }
    }

    private static Node findElementNode(Node[] parent, Element el) {
        try {
            JavaMetamodel.getDefaultRepository().beginTrans(false);
            try {
                for (int i = 0; i < parent.length; i++) {
                    Node node = parent[i];
                    Element _el = findElement(node);
                    if (_el == el) {
                        return node;
                    }
                }
            } finally {
                JavaMetamodel.getDefaultRepository().endTrans();
            }
        } catch (JmiException e) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);            
        }
        return null;
    }
    
    private static Node[] getSubNodes(Node parent) {
        return parent.getChildren().getNodes(true);
    }
    
    public void contentsChanged(ListDataEvent e) {
        //boolean ign = ignoreComboSelect;
        try {
            contentsChanging = true;
            super.contentsChanged(e);
        } finally {
            contentsChanging = false;
        }
    }
    
    /**
     * extracts an element out of the node. This must be wrapped in a mdr transaction 
     * @param n the node to look up in
     * @return the element or null
     */ 
    private static Element findElement(Node n) {
        Element el = null;
        JMIElementCookie c = (JMIElementCookie) n.getCookie(JMIElementCookie.class);
        if (c != null) {
            el = c.getElement();
        } else {
            org.openide.src.Element srcEl = (org.openide.src.Element)
                    n.getCookie(org.openide.src.Element.class);
            if (srcEl != null) {
                el = (Element) BridgeUtils.getJavaObject(srcEl);
            } else {
                // in case it is not hierarchy node
                DataObject d = (DataObject) n.getCookie(DataObject.class);
                if (d != null) {
                    el = JavaMetamodel.getManager().getResource(d.getPrimaryFile());
                }
            }
        }
        
       if (el==null || !el.isValid()) 
            return null;
        return el;
    }
    
    private class RootL extends NodeListModel implements PropertyChangeListener {
        public void setSelectedItem(Object item) {
            if (!contentsChanging)
                super.setSelectedItem(item);
        }

        public void propertyChange(PropertyChangeEvent e) {
            Object source = e.getSource();
            String propName = e.getPropertyName();
            if ((source == topComponent ||
                (topComponent == null && source == TopComponent.getRegistry())) &&
                TopComponent.Registry.PROP_ACTIVATED_NODES.equals(propName)
                ) {
                // node changed !!! sync on the SourceCookie.Editor cookie.
                if (!ignoreActivatedNodes) {
                    try {
                        ignoreComboSelect = true;
                        activatedNodeChanged(getActivatedNode());
                    } finally {
                        ignoreComboSelect = false;
                    }
                }
            } else if (source == getExplorerManager() && 
                ExplorerManager.PROP_SELECTED_NODES.equals(propName)) {
                // something was selected ??
                // HACK! HACK!
                if (("1.4".compareToIgnoreCase(System.getProperty("java.version")) > 0) && !ignoreComboSelect) {
                    boolean saveActivatedNodes = ignoreActivatedNodes;
                    try {
                        ignoreActivatedNodes = true;
                        choiceItemSelected((Node[])e.getNewValue());
                    } finally {
                        ignoreActivatedNodes = saveActivatedNodes;
                    }
                }
            }
        }
    }

    private class SelectionManager extends NameNavigator
        implements JComboBox.KeySelectionManager {

        SelectionManager() {
            super(ExplorerManager.find(NavigationView.this));
        }

        public int selectionForKey(char key, ComboBoxModel comboBoxModel) {
            Object selectedItem = comboBoxModel.getSelectedItem();
            // suppose the selected object is a visualizer.
            Node currentNode = Visualizer.findNode(selectedItem);

            checkTimeout();
            Node result = navigate(currentNode, key);
            if (result == null)
                return -1;

            int index = ((NodeListModel)comboBoxModel).getIndex(Visualizer.findVisualizer(result));
            return index;
        }
    }

    class OpenCookieImpl implements OpenCookie {
        Node n;

        OpenCookieImpl(Node n) {
            this.n = n;
        }

        public void open() {
            JEditorPane[] panes = sourceCookie.getOpenedPanes();

            if (panes != null && topComponent != null) {
                for (int i = 0; i < panes.length; i++) {
                    if (topComponent.isAncestorOf(panes[i])) {
                        openInPane(panes[i]);
                        return;
                    }
                }
            }
            OpenCookie ck = (OpenCookie)n.getCookie(OpenCookie.class);
            if (ck != null)
                ck.open();
        }

        void openInPane(JEditorPane p) {
            Element el = null;
            try {
                JavaMetamodel.getDefaultRepository().beginTrans(false);
                try {
                    el = findElement(n);
                } finally {
                    JavaMetamodel.getDefaultRepository().endTrans();
                }
            } catch (JmiException e) {
                ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
            }

            if (el != null) {
                int offset = JavaMetamodel.getManager().getElementPosition(el).getBegin().getOffset();
                p.getCaret().setDot(offset);
                p.requestFocus();
            }
        }
    }

    class FNode extends FilterNode {
        OpenCookieImpl      openCookie;

        FNode(Node original, Children ch) {
            super(original, ch);
        }

        public Node.Cookie getCookie(Class ck) {
            if (ck == OpenCookie.class) {
                if (openCookie != null)
                    return openCookie;
                OpenCookie orig = (OpenCookie)getOriginal().getCookie(OpenCookie.class);
                if (orig == null)
                    return null;
                return openCookie = new OpenCookieImpl(getOriginal());
            }
            return super.getCookie(ck);
        }
    }

    class FChildren extends FilterNode.Children {
        
        FChildren(Node n) {
            super(n);
        }

        protected Node[] createNodes(Object k) {
            if (!(k instanceof Node))
                return new Node[] { };
            Node n = (Node)k;
            return new Node[] { new FNode(n, new FChildren(n)) };
        }
        
        Resource getResource() {
            return ((SourceChildren)original.getChildren()).getElement();
        }
        
        void updateResource() {
            Resource oldRes = getResource();
            Resource newRes = JavaMetamodel.getManager().getResource(dataObject.getPrimaryFile());
            if (oldRes != newRes) {
                ((SourceChildren) original.getChildren()).setElement(newRes);
            }
        }
        
    }
    
    /* The listener for listening on extent unmount */
    private static final class ExtentListener extends WeakReference implements MDRChangeListener, Runnable {

        private final MDRChangeSource source;

        public ExtentListener(NavigationView referent, MDRChangeSource source) {
            super(referent, Utilities.activeReferenceQueue());
            this.source = source;
        }

        public void change(MDRChangeEvent e) {
            NavigationView nv = (NavigationView) get();
            if (nv == null) return;
            if ((e instanceof ExtentEvent) && ((ExtentEvent) e).getType() == ExtentEvent.EVENT_EXTENT_DELETE) {
                nv.checkRootContext();
            }
        }

        public void run() {
            source.removeListener(this);
        }

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