Source code file content

Revision: 2

import
» Project Revision History

» Checkout URL

web-content / trunk / docs / hierarchy-tutorial / tutorial.html

Size: 19596 bytes, 1 line
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
	<TITLE>C/C++ language model API tutorial. Create C/C++ Navigator.</TITLE>
	<META NAME="GENERATOR" CONTENT="StarOffice 7  (Solaris x86)">
	<META NAME="AUTHOR" CONTENT="Alexander Simon">
</HEAD>
<link rel="stylesheet" type="text/css" href="http://www.netbeans.org/netbeans.css">
<BODY LANG="en-US" DIR="LTR">

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

<p>In this tutorial, you will learn how to add Class Hierarchy View based on C/C++ language model.
<p>Once this tutorial is finished, you will have a View that provides hierarchy structure of currently selected C/C++ class.

<p><h2><a name="gettingtoknowthesample"></a>Creating module.</h2>

<ol>
<p><li>Choose File &gt; New Project. In the New Project wizard, choose
<tt>NetBeans Plug-in Modules</tt> under Categories and <tt>Module Project</tt>
under Projects. Click Next. Type <tt>ClassHierarchy</tt> 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.

<p><li>Type <tt>org.netbeans.mycppextension</tt> in Code Name Base and <tt>C/C++
Class Hierararchy Tree View</TT> in Module Display Name. Click Finish.
<p><li>Right-click the project, choose Properties, click
Libraries in the Project Properties dialog box and declare a
dependency on the following APIs: 
<P><UL>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-actions/overview-summary.html">
            <FONT COLOR="#3333ff">Actions API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-explorer/overview-summary.html">
            <FONT COLOR="#3333ff">Explorer and Property Sheet API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-nodes/overview-summary.html">
            <FONT COLOR="#3333ff">Nodes API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="../org-netbeans-modules-cnd-api-model/overview-summary.html">
            <FONT COLOR="#3333ff">C/C++ Code Model API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-loaders/overview-summary.html">
            <FONT COLOR="#3333ff">Datasystems API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-netbeans-modules-editor/overview-summary.html">
            <FONT COLOR="#3333ff">Editor</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-netbeans-modules-editor-lib/overview-summary.html">
            <FONT COLOR="#3333ff">Editor Library</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-filesystems/overview-summary.html">
            <FONT COLOR="#3333ff">File System API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-text/overview-summary.html">
            <FONT COLOR="#3333ff">Text API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-awt/overview-summary.html">
            <FONT COLOR="#3333ff">UI Utilites API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-util/overview-summary.html">
            <FONT COLOR="#3333ff">Utilites API</FONT></A></P>
	<LI><P STYLE="margin-bottom: 0in"><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-windows/overview-summary.html">
            <FONT COLOR="#3333ff">Windows System API</FONT></A></P>
</UL>
<p><li>Right-click the project, choose Properties, click
Libraries in the Project Properties dialog box and declare a
dependency on the Non-API module (check <tt>Show Non-API Modules</tt>): 
<P><UL>
	<LI><P STYLE="margin-bottom: 0in">
            <FONT COLOR="#3333ff">C/C++ Code Model Utilities</FONT></P>
</UL>
<p>Edit <tt>C/C++ Code Model Utilities</tt> module dependency.
Set <tt>Imlpementation version</tt>.
In future module will be added in API.
</ol>

<!-- ===================================================================================== -->

<p><h2><a name="gettingtoknowthesample"></a>Creating editor action.</h2>

<ol>
<p><li>Right-click the module project, choose New &gt; Action and
choose <tt>Conditionally Enabled (use CookieAction)</tt> and
<tt>EditorCookie</tt> in the drop-down list. Click Next.
<p>Choose <TT>Edit</TT> in the Category,
choose <tt>Navigate</tt> in the drop-down list <tt>Menu</tt>,
select <tt>EditorContext Menu Item</tt> and
choose <tt>text/x-c++</tt> in the drop-down list <tt>Content Type</tt>. Click Next.
<p>Type <tt>DescendantClasses</tt> in the Class Name,
<tt>Descendant Classes</tt> in the Display Name. Click Finish.
<p><li>Right-click the project
node and choose <tt>Install/Reload in Development IDE</tt>. If a
warning message appears, click OK. When the module installs, look
under the <tt>Navigate</tt> menu and you will find a new menu item called <tt>Desendant Classes</tt>
</ol>

<!-- ===================================================================================== -->

<p><h2><a name="gettingtoknowthesample"></a>Creating hierarchy view.</h2>

<ol>
<p><li>Right-click the module project, choose New &gt; Window Component. Click Next.
<p>Type <tt>ClassHierarchy</tt> in the Class Name Prefix. Click Finish.

<p><li>Open <TT>ClassHierarchyTopComponent.java</TT> in the Design view.
Right-click in the TopComponent, choose Set Layout, and select
BorderLayout.

<p><li>Use the Palette (Ctrl-Shift-8) to drop a
<TT>JScrollPane</TT> on <TT>ClassHierarchyTopComponent.java</TT>.
Right-click the <TT>JScrollPane</TT>, choose Change
Variable Name and type <TT>hierarchyPane</TT>.

<p><li>Open the Inspector, if it isn't open. (Use the Window menu.) In the Inspector,
select the <TT>hierarchyPane</TT>, open the Properties window
(Ctrl-Shift-7), click the &quot;Code&quot; tab, and add this line to
the Custom Creation Code property (the very last property in the
list): 
<p><pre class="examplecode">
    new <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-explorer/org/openide/explorer/view/BeanTreeView.html">BeanTreeView()</a>;
</pre>
<P>BeanTreeView is one of several views provided by the Explorer and
Property Sheet API.

<p><li>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 &quot;Explorer and Property Sheet API&quot; will cause the import
statement for the BeanTreeView being generated for you by the
IDE.

<p><li>In the <TT>Bundle.properties</TT> file, change the
<tt>CTL_ClassHierarchyAction</tt> and <tt>CTL_ClassHierarchyTopComponent</tt> keys to the
value <tt>C++ Class Hierarchy</tt>.

<p><li>Open the <TT>ClassHierarchyTopComponent.java</TT> in the Source view and add
<TT>implements ExplorerManager.Provider</TT> to the signature at the
top of the class.

<p><li>Next, instantiate the <TT><A HREF="http://www.netbeans.org/download/dev/javadoc/org-openide-explorer/org/openide/explorer/ExplorerManager.html">ExplorerManager</A></TT>
as a transient object: 
<p><pre class="examplecode">
    private transient ExplorerManager explorerManager = new ExplorerManager();
</PRE>

<p><li>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 <TT>getExplorerManager()</TT> as follows: 
<p><pre class="examplecode">
        public ExplorerManager getExplorerManager() {
             return explorerManager;
        }
</PRE>

<p><li>Create field:
<p><pre class="examplecode">
        private AbstractNode root;
</PRE>

<p><li>Now go to the Constructor and add the following after the last existing line:
<p><pre class="examplecode">
        ((BeanTreeView)hierarchyPane).setRootVisible(false);
        Children.Array children = new Children.SortedArray();
        root = new AbstractNode(children);
        getExplorerManager().setRootContext(root);
</PRE>

<p><li>Fix imports. Select <tt>org.openide.nodes.Children</tt> for <tt>Children</tt>.

</ol>

<!-- ===================================================================================== -->

<p><h2><a name="gettingtoknowthesample"></a>Create node and nodes support.</h2>

<ol>
<p><li>Create class called <tt>HierarchyNode.java</tt> and add following content:
<p><pre class="examplecode">
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;
    }
}
</PRE>
<p>Class extends <tt>AbstractCsmNode</tt> and implements Comparable interface. 
Class nodes use Children.SortedArray for children array.
Comparable interface allow sorting nodes by name.
<p>Both class <tt>HierarchyNode</tt> constructors are private. Static factory method <tt>nodeFactory(CsmClass, Map)</tt>
is responsible for node creation.

<p><li>Create class called <tt>ContextUtils.java</tt> and add following content:
<p><pre class="examplecode">
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;
    }
}
</PRE>
<p>Utility class helps to find model declaration by offset.
</ol>

<!-- ===================================================================================== -->

<p><h2><a name="gettingtoknowthesample"></a>C/C++ language support.</h2>
Modify class <tt>ClassHierarchyTopComponent</tt>.
<ol>
<p><li>Create field that will keep model class:
<p><pre class="examplecode">
    private <A HREF="../org-netbeans-modules-cnd-api-model/org/netbeans/modules/cnd/api/model/CsmClass.html">CsmClass</A> csmClass;
</PRE>

<p><li>Create methods that setup tree content:
<p><pre class="examplecode">
    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());
                    }
                });
            }
        }
    }
</PRE>
    
<p><li>Create methods that pass through project classes and build hierarchy:
<p><pre class="examplecode">
    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);
                        }
                    }
                }
            }
        }
    }
</PRE>

<p><li>Fix imports. Select <tt>java.util.Iterator</tt> for <tt>Iterator</tt>,
<tt>java.util.Set</tt> for <tt>Set</tt>, 
<tt>java.util.List</tt> for <tt>List</tt> and 
<tt>org.openide.nodes.Node</tt> for <tt>Node</tt>.
</ol>

<p>Modify class <tt>DescendantClasses</tt>.
<ol>
<p><li>Add language support in constructor. Replace constructor:
<p><pre class="examplecode">
    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();
        }
    }
</PRE>

<p><li>Override method <tt>enable</tt>:
<p><pre class="examplecode">
    protected boolean enable(Node[] activatedNodes) {
        CsmDeclaration decl = ContextUtils.findDeclaration(activatedNodes[0]);
        if (decl != null && CsmKindUtilities.isClass(decl)){
            return true;
        }
        return false;
    }
</PRE>

<p><li>Fix imports.

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

<IMG SRC="api.jpg" NAME="using C/C++ model API" ALIGN=BOTTOM BORDER=0><BR><BR>

<!-- ===================================================================================== -->

<p><h2><a name="gettingtoknowthesample"></a>Downloads</h2>
<p>Download <a href="ClassHierarchy.zip">NetBeans Project</a> or 
<a href="org-netbeans-mycppextension-classhierarchy.nbm">NetBeans Module</a>.
</BODY>
</HTML>

Project Features

About this Project

CND was started in November 2009, is owned by DimaZh, and has 197 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