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):
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.
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: