C/C++ Pack, Language Model and C/C++ Class Hierarchy View Tutorial

In this tutorial, you will learn how to add Class Hierarchy View based on C/C++ language model.

Once this tutorial is finished, you will have a View that provides hierarchy structure of currently selected C/C++ class.

Creating module.

  • Choose File > New Project. In the New Project wizard, choose NetBeans Plug-in Modules under Categories and Module Project under Projects. Click Next. Type ClassHierarchy 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++ Class Hierararchy 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.

    Creating editor action.

  • Right-click the module project, choose New > Action and choose Conditionally Enabled (use CookieAction) and EditorCookie in the drop-down list. Click Next.

    Choose Edit in the Category, choose Navigate in the drop-down list Menu, select EditorContext Menu Item and choose text/x-c++ in the drop-down list Content Type. Click Next.

    Type DescendantClasses in the Class Name, Descendant Classes in the Display Name. Click Finish.

  • 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 Navigate menu and you will find a new menu item called Desendant Classes

    Creating hierarchy view.

  • Right-click the module project, choose New > Window Component. Click Next.

    Type ClassHierarchy in the Class Name Prefix. Click Finish.

  • Open ClassHierarchyTopComponent.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 ClassHierarchyTopComponent.java. Right-click the JScrollPane, choose Change Variable Name and type hierarchyPane.

  • Open the Inspector, if it isn't open. (Use the Window menu.) In the Inspector, select the hierarchyPane, 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_ClassHierarchyAction and CTL_ClassHierarchyTopComponent keys to the value C++ Class Hierarchy.

  • Open the ClassHierarchyTopComponent.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)hierarchyPane).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 HierarchyNode.java and add following content:

    import java.awt.Image;
    import java.awt.event.ActionEvent;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import org.netbeans.modules.cnd.api.model.CsmClass;
    import org.netbeans.modules.cnd.api.model.CsmObject;
    import org.netbeans.modules.cnd.api.model.CsmOffsetable;
    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.nodes.Node;
    
    /**
     * Hierarchy Tree node.
     */
    public class HierarchyNode extends AbstractCsmNode implements Comparable {
        private Image icon;
        private CsmObject object;
        private HierarchyNode(CsmClass element) {
            this(new Children.SortedArray(),element);
        }
        
        private HierarchyNode(Children children, CsmClass element) {
            super(children);
            setName(element.getName());
            object = element;
        }
    
        public CsmObject getCsmObject() {
            return object;
        }
        
        public int compareTo(Object o) {
            if( o instanceof HierarchyNode ) {
                return getDisplayName().compareTo(((HierarchyNode) o).getDisplayName());
            }
            return 0;
        }
        
        public Action getPreferredAction() {
            if (CsmKindUtilities.isOffsetable(object)){
                return new AbstractAction(){
                    public void actionPerformed(ActionEvent e) {
                        CsmUtilities.openSource((CsmOffsetable)object);
                    }
                };
            }
            return super.getPreferredAction();
        }
        
        public static HierarchyNode nodeFactory(final CsmClass cls, final Map map){
            HierarchyNode node = null;
            Set set = (Set)map.get(cls);
            if (set == null){
                node = new HierarchyNode(Children.LEAF,cls);
            } else {
                node = new HierarchyNode(cls);
                for(Iterator it = set.iterator(); it.hasNext();){
                    final CsmClass c = (CsmClass)it.next();
                    final Children children = node.getChildren();
                    children.MUTEX.writeAccess(new Runnable(){
                        public void run() {
                            children.add(new Node[]{nodeFactory(c, map)});
                        }
                    });
                    
                }
            }
            return node;
        }
    }
    

    Class extends AbstractCsmNode and implements Comparable interface. Class nodes use Children.SortedArray for children array. Comparable interface allow sorting nodes by name.

    Both class HierarchyNode constructors are private. Static factory method nodeFactory(CsmClass, Map) is responsible for node creation.

  • Create class called ContextUtils.java and add following content:

    import java.util.Iterator;
    import javax.swing.JEditorPane;
    import org.netbeans.modules.cnd.api.model.CsmClass;
    import org.netbeans.modules.cnd.api.model.CsmDeclaration;
    import org.netbeans.modules.cnd.api.model.CsmFile;
    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.util.CsmKindUtilities;
    import org.netbeans.modules.cnd.modelutil.CsmUtilities;
    import org.openide.cookies.EditorCookie;
    import org.openide.nodes.Node;
    
    public class ContextUtils {
        
        private ContextUtils() {
        }
        
        public static CsmDeclaration findDeclaration(Node activatedNode) {
            EditorCookie c = (EditorCookie) activatedNode.getCookie(EditorCookie.class);
            if (c != null) {
                JEditorPane[] panes = c.getOpenedPanes();
                if (panes != null && panes.length>0) {
                    int offset = panes[0].getCaret().getDot();
                    CsmFile file = CsmUtilities.getCsmFile(activatedNode,false);
                    if (file != null){
                        return findInnerFileDeclaration(file, offset);
                    }
                }
            }
            return null;
        }
        
        private static CsmDeclaration findInnerFileDeclaration(CsmFile file, int offset) {
            CsmDeclaration innerDecl = null;
            for (Iterator it = file.getDeclarations().iterator(); it.hasNext();) {
                CsmDeclaration decl = (CsmDeclaration) it.next();
                if (isInObject(decl, offset)) {
                    innerDecl = findInnerDeclaration(decl, offset);
                    innerDecl = innerDecl != null ? innerDecl : decl;
                    break;
                }
            }
            return innerDecl;
        }
    
        private static CsmDeclaration findInnerDeclaration(CsmDeclaration outDecl, int offset) {
            Iterator it = null;
            CsmDeclaration innerDecl = null;
            if (CsmKindUtilities.isNamespaceDefinition(outDecl)) {
                it = ((CsmNamespaceDefinition) outDecl).getDeclarations().iterator();
            } else if (CsmKindUtilities.isClass(outDecl)) {
                CsmClass cl  = (CsmClass)outDecl;
                it = cl.getMembers().iterator();
            }
            if (it != null) {
                while (it.hasNext()) {
                    CsmDeclaration decl = (CsmDeclaration) it.next();
                    if (isInObject(decl, offset)) {
                        innerDecl = findInnerDeclaration(decl, offset);
                        innerDecl = innerDecl != null ? innerDecl : decl;
                        break;
                    }
                }
            }
            return innerDecl;
        }    
    
        private static boolean isInObject(CsmObject obj, int offset) {
            if (!CsmKindUtilities.isOffsetable(obj)) {
                return false;
            }
            CsmOffsetable offs = (CsmOffsetable)obj;
            if ((offs.getStartOffset() <= offset) &&
                    (offset <= offs.getEndOffset())) {
                return true;
            }
            return false;
        }
    }
    

    Utility class helps to find model declaration by offset.

    C/C++ language support.

    Modify class ClassHierarchyTopComponent.

  • Create field that will keep model class:

        private CsmClass csmClass;
    

  • Create methods that setup tree content:

        public void setClass(CsmClass cls){
            csmClass = cls;
            update();
        }
        
        private synchronized void update() {
            if (csmClass != null){
                final Children children = root.getChildren();
                if (!children.MUTEX.isReadAccess()){
                    children.MUTEX.writeAccess(new Runnable(){
                        public void run() {
                            children.remove(children.getNodes());
                            Node node = HierarchyNode.nodeFactory(csmClass, buildHierarchy(csmClass));
                            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());
                        }
                    });
                }
            }
        }
    

  • Create methods that pass through project classes and build hierarchy:

        private HashMap buildHierarchy(CsmClass cls){
            HashMap map = new HashMap();
            CsmProject prj = cls.getContainingFile().getProject();
            buildHierarchy(prj.getGlobalNamespace(), map);
            return map;
        }
        
        private void buildHierarchy(CsmNamespace ns, HashMap map){
            for(Iterator it = ns.getNestedNamespaces().iterator(); it.hasNext();){
                buildHierarchy((CsmNamespace)it.next(), map);
            }
            for(Iterator it = ns.getDeclarations().iterator(); it.hasNext();){
                CsmDeclaration decl = (CsmDeclaration)it.next();
                if (CsmKindUtilities.isClass(decl)){
                    CsmClass cls = (CsmClass)decl;
                    List list = cls.getBaseClasses();
                    if (list != null && list.size() >0){
                        for(int i = 0; i < list.size(); i++){
                            CsmInheritance inh = (CsmInheritance)list.get(i);
                            CsmClass c = inh.getCsmClass();
                            if (c != null) {
                                Set back = (Set)map.get(c);
                                if (back == null){
                                    back = new HashSet();
                                    map.put(c,back);
                                }
                                back.add(cls);
                            }
                        }
                    }
                }
            }
        }
    

  • Fix imports. Select java.util.Iterator for Iterator, java.util.Set for Set, java.util.List for List and org.openide.nodes.Node for Node.

    Modify class DescendantClasses.

  • Add language support in constructor. Replace constructor:

        protected void performAction(Node[] activatedNodes) {
            CsmDeclaration decl = ContextUtils.findDeclaration(activatedNodes[0]);
            if (decl != null && CsmKindUtilities.isClass(decl)){
                ClassHierarchyTopComponent view = ClassHierarchyTopComponent.findInstance();
                view.setClass((CsmClass)decl);
                view.open();
                view.requestActive();
            }
        }
    

  • Override method enable:

        protected boolean enable(Node[] activatedNodes) {
            CsmDeclaration decl = ContextUtils.findDeclaration(activatedNodes[0]);
            if (decl != null && CsmKindUtilities.isClass(decl)){
                return true;
            }
            return false;
        }
    

  • Fix imports.

  • Right-click the project node and choose Install/Reload in Development IDE. Open C/C++ project. Open Class Hierarhy window. Click context menu on class header in editor and select Desendant Classes. You can see class inheritance:

    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