C/C++ Pack, Language Model and C/C++ Navigator Tutorial

In this tutorial, you will learn how to create a BeanTreeView navigator based on C/C++ language model.

Once this tutorial is finished, you will have a BeanTreeView navigator that provides structure of currently edited C/C++ file.

Creating module and navigator view.

  • Choose File > New Project. In the New Project wizard, choose NetBeans Plug-in Modules under Categories and Module Project under Projects. Click Next. Type CppNavigatorTreeView in Project Name and set Project Location to an appropriate folder on your disk. If they are not selected, select Standalone Module and Set as Main Project. Click Next.

  • Type org.netbeans.mycppextension in Code Name Base and C/C++ Navigator Tree View in Module Display Name. Click Finish.

  • Right-click the project, choose Properties, click Libraries in the Project Properties dialog box and declare a dependency on the following APIs:

  • Right-click the project, choose Properties, click Libraries in the Project Properties dialog box and declare a dependency on the Non-API module (check Show Non-API Modules):

    • C/C++ Code Model Utilities

    Edit C/C++ Code Model Utilities module dependency. Set Imlpementation version. In future module will be added in API.

  • Right-click the module project, choose New > File/Folder and choose Window Component from the NetBeans Module Development category. Click Next. Choose navigator in the drop-down list and select Open on Application Start. Click Next.

  • Type CppNavigator in Class Name Prefix. Optionally, add an icon with a dimension of 16x16 pixels. Click Finish.

  • Open CppNavigatorTopComponent.java in the Design view. Right-click in the TopComponent, choose Set Layout, and select BorderLayout.

  • Use the Palette (Ctrl-Shift-8) to drop a JScrollPane on CppNavigatorTopComponent.java. Right-click the JScrollPane, choose Change Variable Name and type navigatorPane.

  • Open the Inspector, if it isn't open. (Use the Window menu.) In the Inspector, select the navigatorPane, open the Properties window (Ctrl-Shift-7), click the "Code" tab, and add this line to the Custom Creation Code property (the very last property in the list):

        new BeanTreeView();
    

    BeanTreeView is one of several views provided by the Explorer and Property Sheet API.

  • Click the Source toggle button in the GUI Builder. Right-click in the Source Editor, and choose Fix imports (Alt-Shift-F). The dependency you set on "Explorer and Property Sheet API" will cause the import statement for the BeanTreeView being generated for you by the IDE.

  • In the Bundle.properties file, change the CTL_CppNavigatorTopComponent and CTL_CppNavigatorAction keys to the value C/C++ Navigator.

  • Right-click the project node and choose Install/Reload in Development IDE. If a warning message appears, click OK. When the module installs, look under the Window menu and you will find a new menu item called C/C++ Navigator, at the top of the list of menu items. Choose it and you will see the start of your navigator view.

    Setup an explorer manager.

  • Open the CppnavigatorTopComponent.java in the Source view and add implements ExplorerManager.Provider to the signature at the top of the class.

  • Next, instantiate the ExplorerManager as a transient object:

        private transient ExplorerManager explorerManager = new ExplorerManager();
    

  • Place the cursor in the signature. A light bulb will prompt you to let the IDE insert an import statement and implement the abstract methods. Follow its advice, by clicking on the suggestion, and then fill out the generated getExplorerManager() as follows:

            public ExplorerManager getExplorerManager() {
                 return explorerManager;
            }
    

  • Create field:

            private AbstractNode root;
    

  • Now go to the Constructor and add the following after the last existing line:

            ((BeanTreeView)navigatorPane).setRootVisible(false);
            Children.Array children = new Children.SortedArray();
            root = new AbstractNode(children);
            getExplorerManager().setRootContext(root);
    

  • Fix imports. Select org.openide.nodes.Children for Children.

    Create node and nodes support.

    Create class called CppDeclarationNode.java and add following content:

    import java.awt.Image;
    import java.awt.event.ActionEvent;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import org.netbeans.modules.cnd.api.model.CsmClass;
    import org.netbeans.modules.cnd.api.model.CsmClassifier;
    import org.netbeans.modules.cnd.api.model.CsmDeclaration;
    import org.netbeans.modules.cnd.api.model.CsmEnum;
    import org.netbeans.modules.cnd.api.model.CsmEnumerator;
    import org.netbeans.modules.cnd.api.model.CsmFunction;
    import org.netbeans.modules.cnd.api.model.CsmInclude;
    import org.netbeans.modules.cnd.api.model.CsmMacro;
    import org.netbeans.modules.cnd.api.model.CsmMember;
    import org.netbeans.modules.cnd.api.model.CsmNamespaceDefinition;
    import org.netbeans.modules.cnd.api.model.CsmObject;
    import org.netbeans.modules.cnd.api.model.CsmOffsetable;
    import org.netbeans.modules.cnd.api.model.CsmParameter;
    import org.netbeans.modules.cnd.api.model.CsmType;
    import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
    import org.netbeans.modules.cnd.modelutil.AbstractCsmNode;
    import org.netbeans.modules.cnd.modelutil.CsmUtilities;
    import org.openide.nodes.Children;
    import org.openide.util.Utilities;
    
    /**
     * Navigator Tree node.
     */
    public class CppDeclarationNode extends AbstractCsmNode implements Comparable {
        private Image icon;
        private CsmObject object;
        private CppDeclarationNode(CsmObject element) {
            super(new NavigatorChildren(element));
            object = element;
        }
        
        private CppDeclarationNode(Children children, CsmObject element) {
            super(children);
            object = element;
        }
    
        public CsmObject getCsmObject() {
            return object;
        }
        
        public int compareTo(Object o) {
            if( o instanceof CppDeclarationNode ) {
                return getDisplayName().compareTo(((CppDeclarationNode) o).getDisplayName());
            }
            return 0;
        }
        
        public void setIcon(Image icon) {
            this.icon = icon;
        }
        
        public Image getIcon(int param) {
            if (icon != null){
                return icon;
            }
            return super.getIcon(param);
        }
        
        public Image getOpenedIcon(int param) {
            return getIcon(param);
        }
        
        public Action getPreferredAction() {
            if (CsmKindUtilities.isOffsetable(object)){
                return new AbstractAction(){
                    public void actionPerformed(ActionEvent e) {
                        CsmUtilities.openSource((CsmOffsetable)object);
                    }
                };
            }
            return super.getPreferredAction();
        }
        
        public static CppDeclarationNode nodeFactory(CsmObject element){
            CppDeclarationNode node = null;
            if (CsmKindUtilities.isClassifier(element)){
                node = new CppDeclarationNode(element);
                node.setName(((CsmClassifier)element).getName());
            } else if(CsmKindUtilities.isNamespaceDefinition(element)){
                node = new CppDeclarationNode(element);
                node.setName(((CsmNamespaceDefinition)element).getName());
            } else if(CsmKindUtilities.isDeclaration(element)){
                node = new CppDeclarationNode(Children.LEAF,element);
                node.setName(((CsmDeclaration)element).getName());
                if(CsmKindUtilities.isFunction(element)){
                    node.setName(getSignature((CsmFunction)element));
                } else {
                    node.setName(((CsmDeclaration)element).getName());
                }
            } else if(CsmKindUtilities.isEnumerator(element)){
                node = new CppDeclarationNode(Children.LEAF,element);
                node.setName(((CsmEnumerator)element).getName());
            } else if(CsmKindUtilities.isMacro(element)){
                node = new CppDeclarationNode(Children.LEAF,element);
                node.setName(((CsmMacro)element).getName());
                node.setIcon(Utilities.loadImage("org/netbeans/mycppextension/cppnavigatortreeview/directive.png", true));
            } else if(element instanceof CsmInclude){
                node = new CppDeclarationNode(Children.LEAF,element);
                node.setName(((CsmInclude)element).getIncludeName());
                node.setIcon(Utilities.loadImage("org/netbeans/mycppextension/cppnavigatortreeview/directive.png", true));
            }
            return node;
        }
        
        private static String getSignature(CsmFunction fun) {
            StringBuffer sb = new StringBuffer(fun.getName());
            sb.append('(');
            boolean addComma = false;
            for( Iterator iter = fun.getParameters().iterator(); iter.hasNext(); ) {
                CsmParameter par = (CsmParameter) iter.next();
                if( addComma ) {
                    sb.append(", ");
                } else {
                    addComma = true;
                }
                CsmType type = par.getType();
                if( type != null ) {
                    sb.append(type.getText());
                    sb.append(' ');
                }
                sb.append(par.getName());
            }
            sb.append(')');
            return sb.toString();
        }
    
        private static class NavigatorChildren extends Children.SortedArray {
            private CsmObject element;
            public NavigatorChildren(CsmObject element){
                this.element = element;
            }
            
            protected Collection initCollection() {
                Collection retValue = new ArrayList();
                if (CsmKindUtilities.isClass(element)){
                    CsmClass cls = (CsmClass)element;
                    for (Iterator it = cls.getMembers().iterator(); it.hasNext();){
                        CppDeclarationNode node = nodeFactory((CsmMember)it.next());
                        if (node != null){
                            retValue.add(node);
                        }
                    }
                } else if (CsmKindUtilities.isEnum(element)){
                    CsmEnum cls = (CsmEnum)element;
                    for (Iterator it = cls.getEnumerators().iterator(); it.hasNext();){
                        CppDeclarationNode node = nodeFactory((CsmEnumerator)it.next());
                        if (node != null){
                            retValue.add(node);
                        }
                    }
                } else if(CsmKindUtilities.isNamespaceDefinition(element)){
                    CsmNamespaceDefinition ns = (CsmNamespaceDefinition)element;
                    for (Iterator it = ns.getDeclarations().iterator(); it.hasNext();){
                        CppDeclarationNode node = nodeFactory((CsmDeclaration)it.next());
                        if (node != null){
                            retValue.add(node);
                        }
                    }
                }
                return retValue;
            }
        }
    }
    

    Class extends AbstractCsmNode and override methods:

        getIcon(int) {
        getOpenedIcon(int)
        getPreferredAction()
    

    Method getPreferredAction() uses implementation of module C/C++ Code Model Utilities to open model element in editor.

    Also class implements Comparable interface. Classifier and name space nodes use Children.SortedArray for children array. Comparable interface allow sorting nodes by name.

    Inner class NavigatorChildren extends Children.SortedArray and is used for children array. Class override method:

        protected Collection initCollection()
    

    Method responsible for lazy filling classes and name spaces nodes' content.

    Both class CppDeclarationNode constructors are private. Static factory method nodeFactory(CsmObject) is responsible for node creation.

    C/C++ language support.

    Modify class CppNavigatorTopComponent.

  • Add in implements list two interfaces:

            PropertyChangeListener, CsmModelListener 
    

    and implements abstract methods.

  • Create field that will keep model file:

        private CsmFile csmFile;
    

  • At first we needed register and unregister component and model listeners. Replace methods:

        public void componentOpened() {
            TopComponent.getRegistry().addPropertyChangeListener(this);
            CsmModelAccessor.getModel().addModelListener(this);
            checkNodes();
        }
        
        public void componentClosed() {
            TopComponent.getRegistry().removePropertyChangeListener(this);
            CsmModelAccessor.getModel().removeModelListener(this);
            setFile(null);
        }
    

  • Create private methods that find model file by active node:

        private void checkNodes(){
            checkNodes(TopComponent.getRegistry().getCurrentNodes());
        }
        
        private void checkNodes(Node[] arr){
            if (arr != null) {
                for (int j = 0; j < arr.length; j++) {
                    CsmFile file = CsmUtilities.getCsmFile(arr[j],false);
                    if (file != null)
                        setFile(file);
                }
            }
        }
    

    Method checkNodes() uses implementation of module C/C++ Code Model Utilities to find model file by node.

  • Create methods that setup navigator content:

        private void setFile(CsmFile file) {
            if (csmFile != file){
                csmFile = file;
                update();
            }
        }
        
        private synchronized void update() {
            if (csmFile != null){
                final Children children = root.getChildren();
                if (!children.MUTEX.isReadAccess()){
                    children.MUTEX.writeAccess(new Runnable(){
                        public void run() {
                            children.remove(children.getNodes());
                            for(Iterator it = csmFile.getIncludes().iterator(); it.hasNext();){
                                CsmInclude element = (CsmInclude)it.next();
                                Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
                                if (node != null){
                                    children.add(new Node[]{node});
                                }
                            }
                            for(Iterator it = csmFile.getMacros().iterator(); it.hasNext();){
                                CsmMacro element = (CsmMacro)it.next();
                                Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
                                if (node != null){
                                    children.add(new Node[]{node});
                                }
                            }
                            for(Iterator it = csmFile.getDeclarations().iterator(); it.hasNext();){
                                CsmDeclaration element = (CsmDeclaration)it.next();
                                Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
                                if (node != null){
                                    children.add(new Node[]{node});
                                }
                            }
                        }
                    });
                }
            } else {
                final Children children = root.getChildren();
                if (!children.MUTEX.isReadAccess()){
                    children.MUTEX.writeAccess(new Runnable(){
                        public void run() {
                            children.remove(children.getNodes());
                        }
                    });
                }
            }
        }
    

    Method update() refreshes navigator tree. Method passes through model file include directives, macros and declarations.

  • Add component support. Replace method:

        public void propertyChange(PropertyChangeEvent evt) {
            if (TopComponent.Registry.PROP_ACTIVATED.equals(evt.getPropertyName())){
                checkNodes();
            }
        }
    

  • Add Model support. Replace methods:

        public void projectOpened(CsmProject project) {
                checkNodes();
        }
        
        public void projectClosed(CsmProject project) {
            if (csmFile != null){
                if (csmFile.getProject() == project){
                    setFile(null);
                }
            }
        }
        
        public void modelChanged(CsmChangeEvent e) {
            if (csmFile != null){
                if (e.getChangedFiles().contains(csmFile)){
                    update();
                }
            } else {
                checkNodes();
            }
        }
    

  • Fix imports. Select java.itil.Iterator for Iterator and org.openide.nodes.Node for Node.

  • Save icon:

    with name directive.png in the package org.netbeans.mycppextension.cppnavigatortreeview.

  • Right-click the project node and choose Install/Reload in Development IDE. Open C/C++ project. You can see edited file declarations in C/C++ Navigator:

    Downloads

    Download NetBeans Project or NetBeans Module.

  • Project Features

    About this Project

    CND was started in November 2009, is owned by DimaZh, and has 191 members.
    By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20160708.bf2ac18). © 2014, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
     
     
    Close
    loading
    Please Confirm
    Close