# HG changeset patch # User Danila Sergeyev # Date 1432123042 -14400 # Node ID 60ad687b099c36d304d5523a7e9a484e0f8ae998 # Parent fa4e987b4f0c39f179377f008468c95ef2a92dc5 bug #196979 Extend the callgraph feature for structure/class elements and global variables diff --git a/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/CallGraphModelFactoryImpl.java b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/CallGraphModelFactoryImpl.java --- a/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/CallGraphModelFactoryImpl.java +++ b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/CallGraphModelFactoryImpl.java @@ -48,7 +48,9 @@ import org.netbeans.modules.cnd.api.model.CsmFile; import org.netbeans.modules.cnd.api.model.CsmFunction; import org.netbeans.modules.cnd.api.model.CsmObject; +import org.netbeans.modules.cnd.api.model.CsmOffsetableDeclaration; import org.netbeans.modules.cnd.api.model.CsmProject; +import org.netbeans.modules.cnd.api.model.CsmVariable; import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities; import org.netbeans.modules.cnd.api.model.xref.CsmReference; import org.netbeans.modules.cnd.api.model.xref.CsmReferenceResolver; @@ -75,7 +77,7 @@ if (activatedNodes == null || activatedNodes.length == 0) { return null; } - CsmFunction function = null; + CsmOffsetableDeclaration declaration = null; CsmProject project = null; CsmReference ref = CsmReferenceResolver.getDefault().findReference(activatedNodes[0]); if (ref == null) { @@ -91,22 +93,24 @@ } CsmObject obj = ref.getReferencedObject(); if (CsmKindUtilities.isFunction(obj)) { - function = (CsmFunction) obj; + declaration = (CsmFunction) obj; + } else if(CsmKindUtilities.isVariable(obj)) { + declaration = (CsmVariable) obj; } else { obj = ref.getClosestTopLevelObject(); if (CsmKindUtilities.isFunction(obj)) { - function = (CsmFunction) obj; + declaration = (CsmFunction) obj; } } - if (function != null) { - return new CallModelImpl(project, function); + if (declaration != null) { + return new GeneralCallModelImpl(project, declaration); } return null; } @Override public CallGraphUI getUI(CallModel model) { - if (model instanceof CallModelImpl) { + if (model instanceof GeneralCallModelImpl) { return new CallGraphUI(){ @Override public boolean showGraph() { diff --git a/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallImpl.java b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallImpl.java new file mode 100644 --- /dev/null +++ b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallImpl.java @@ -0,0 +1,158 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.navigation.callgraph; + +import org.netbeans.modules.cnd.api.model.CsmFunction; +import org.netbeans.modules.cnd.api.model.CsmModelAccessor; +import org.netbeans.modules.cnd.api.model.CsmOffsetableDeclaration; +import org.netbeans.modules.cnd.api.model.CsmVariable; +import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities; +import org.netbeans.modules.cnd.api.model.xref.CsmReference; +import org.netbeans.modules.cnd.api.model.xref.CsmReferenceSupport; +import org.netbeans.modules.cnd.callgraph.api.Call; +import org.netbeans.modules.cnd.callgraph.api.Function; +import org.netbeans.modules.cnd.modelutil.CsmUtilities; + +/** + * + * @author Danila Sergeyev + */ +public class GeneralCallImpl implements Call { + + private final Function owner; + private final Function callee; + private final boolean nameOrder; + private final CsmReference reference; + private final CharSequence description; + private final CharSequence htmlName; + + public GeneralCallImpl(CsmOffsetableDeclaration owner, CsmReference reference, CsmOffsetableDeclaration callee, boolean nameOrder) { + this.nameOrder = nameOrder; + this.owner = implementationResolver(owner); + this.callee = implementationResolver(callee); + this.reference = reference; + this.description = initDescription(); + this.htmlName = initHtmlDisplayName(); + } + + public Object getReferencedCall() { + return reference; + } + + @Override + public void open() { + final String taskName = "Open function call"; //NOI18N + Runnable run = new Runnable() { + @Override + public void run() { + CsmUtilities.openSource(reference); + } + }; + CsmModelAccessor.getModel().enqueue(run, taskName); + } + + @Override + public Function getCallee() { + return callee; + } + + @Override + public Function getCaller() { + return owner; + } + + @Override + public String getHtmlDisplayName() { + if (htmlName != null) { + return htmlName.toString(); + } + return null; + } + + @Override + public String getDescription() { + if (description != null) { + return description.toString(); + } + return null; + } + + @Override + public String toString() { + if (nameOrder) { + return getCallee().getName()+"<-"+getCaller().getName(); // NOI18N + } else { + return getCaller().getName()+"->"+getCallee().getName(); // NOI18N + } + } + + @Override + public int compareTo(Call o) { + if (nameOrder) { + return getCaller().getName().compareTo(o.getCaller().getName()); + } + int diff = reference.getStartOffset() - ((GeneralCallImpl)o).reference.getStartOffset(); + if (diff == 0) { + return getCallee().getName().compareTo(o.getCallee().getName()); + } + return diff; + } + + private CharSequence initHtmlDisplayName() { + return CsmReferenceSupport.getContextLineHtml(reference, true); + } + + private CharSequence initDescription() { + return CsmReferenceSupport.getContextLine(reference); + } + + private static Function implementationResolver(CsmOffsetableDeclaration entity) { + if (CsmKindUtilities.isFunction(entity)) { + return new FunctionImpl((CsmFunction) entity); + } else if (CsmKindUtilities.isVariable(entity)) { + return new VariableImpl((CsmVariable) entity); + } else { + return null; + } + } + +} diff --git a/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallModelImpl.java b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallModelImpl.java new file mode 100644 --- /dev/null +++ b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/GeneralCallModelImpl.java @@ -0,0 +1,477 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.navigation.callgraph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.modules.cnd.api.model.CsmClass; +import org.netbeans.modules.cnd.api.model.CsmClassifier; +import org.netbeans.modules.cnd.api.model.CsmCompoundClassifier; +import org.netbeans.modules.cnd.api.model.CsmDeclaration; +import org.netbeans.modules.cnd.api.model.CsmFile; +import org.netbeans.modules.cnd.api.model.CsmFriend; +import org.netbeans.modules.cnd.api.model.CsmFunction; +import org.netbeans.modules.cnd.api.model.CsmFunctionDefinition; +import org.netbeans.modules.cnd.api.model.CsmMacro; +import org.netbeans.modules.cnd.api.model.CsmMember; +import org.netbeans.modules.cnd.api.model.CsmMethod; +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.CsmOffsetableDeclaration; +import org.netbeans.modules.cnd.api.model.CsmProject; +import org.netbeans.modules.cnd.api.model.CsmScope; +import org.netbeans.modules.cnd.api.model.CsmScopeElement; +import org.netbeans.modules.cnd.api.model.CsmTypedef; +import org.netbeans.modules.cnd.api.model.CsmUID; +import org.netbeans.modules.cnd.api.model.CsmVariable; +import org.netbeans.modules.cnd.api.model.deep.CsmCompoundStatement; +import org.netbeans.modules.cnd.api.model.deep.CsmStatement; +import org.netbeans.modules.cnd.api.model.services.CsmCacheManager; +import org.netbeans.modules.cnd.api.model.services.CsmFileInfoQuery; +import org.netbeans.modules.cnd.api.model.services.CsmFileReferences; +import org.netbeans.modules.cnd.api.model.services.CsmReferenceContext; +import org.netbeans.modules.cnd.api.model.services.CsmVirtualInfoQuery; +import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities; +import org.netbeans.modules.cnd.api.model.util.UIDs; +import org.netbeans.modules.cnd.api.model.xref.CsmReference; +import org.netbeans.modules.cnd.api.model.xref.CsmReferenceKind; +import org.netbeans.modules.cnd.api.model.xref.CsmReferenceRepository; +import org.netbeans.modules.cnd.api.model.xref.CsmReferenceResolver; +import org.netbeans.modules.cnd.callgraph.api.Call; +import org.netbeans.modules.cnd.callgraph.api.CallModel; +import org.netbeans.modules.cnd.callgraph.api.Function; +import org.netbeans.modules.cnd.callgraph.api.ui.CallGraphPreferences; +import org.netbeans.modules.cnd.modelutil.CsmUtilities; +import org.netbeans.modules.cnd.support.Interrupter; + +/** + * + * @author Danila Sergeyev + */ + +public class GeneralCallModelImpl implements CallModel { + + private final CsmProject project; + private final CsmReferenceRepository repository; + private final CsmFileReferences fileReference; + private DeclarationUIN uin; + private String name; + + public GeneralCallModelImpl(CsmProject project, CsmOffsetableDeclaration root) { + this.project = project; + name = root.getName().toString(); + uin = new DeclarationUIN(project, root); + repository = CsmReferenceRepository.getDefault(); + fileReference = CsmFileReferences.getDefault(); + } + + @Override + public Function getRoot() { + CsmOffsetableDeclaration root = uin.getDeclaration(); + if (root != null) { + CsmCacheManager.enter(); + try { + return implementationResolver(root); + } finally { + CsmCacheManager.leave(); + } + } + return null; + } + + @Override + public void setRoot(Function newRoot) { + if (newRoot instanceof FunctionImpl) { + FunctionImpl impl = (FunctionImpl)newRoot; + CsmFunction f = impl.getDefinition(); + if (f != null) { + uin = new DeclarationUIN(project, f); + name = f.getName().toString(); + } + } else if (newRoot instanceof VariableImpl) { + VariableImpl impl = (VariableImpl)newRoot; + CsmVariable v = impl.getVariable(); + if (v != null) { + uin = new DeclarationUIN(project, v); + name = v.getName().toString(); + } + } + } + + @Override + public void update() { + //do nothing + } + + + @Override + public boolean isRootVisible() { + return true; + } + + @Override + public String getName() { + return name; + } + + /** + * declaration()->f() + * @param declaration + * @return + */ + @Override + public List getCallers(Function declaration) { + CsmCacheManager.enter(); + try { + Collection declarations = new ArrayList(); + List res = new ArrayList(); + + if (declaration instanceof FunctionImpl) { + FunctionImpl functionImpl = (FunctionImpl) declaration; + CsmFunction owner = functionImpl.getDeclaration(); + declarations.add(owner); + if (CallGraphPreferences.isShowOverriding()) { + if (CsmKindUtilities.isMethodDeclaration(owner)) { + Collection overrides = CsmVirtualInfoQuery.getDefault().getAllBaseDeclarations((CsmMethod) owner); + declarations.addAll(overrides); + } + } + } else if (declaration instanceof VariableImpl) { + declarations.add(((VariableImpl) declaration).getVariable()); + } else { + return res; + } + + EnumSet kinds = EnumSet.of(CsmReferenceKind.DIRECT_USAGE, CsmReferenceKind.UNKNOWN); + HashMap set = new HashMap(); + HashMap macros = new HashMap(); + for(CsmOffsetableDeclaration decl : declarations) { + if (decl.getContainingFile().isValid()) { + for(CsmReference r : repository.getReferences(decl, project, CsmReferenceKind.ANY_REFERENCE_IN_ACTIVE_CODE, Interrupter.DUMMY)){ + if (r == null) { + continue; + } + if (CsmReferenceResolver.getDefault().isKindOf(r,kinds)) { + CsmFunction o = getFunctionDeclaration(getEnclosingFunction(r)); + if (o != null) { + if (!set.containsKey(o)) { + set.put(o, r); + } + } else { + CsmMacro enclosingMacro = getEnclosingMacro(r); + if (enclosingMacro != null && !macros.containsKey(enclosingMacro)) { + macros.put(enclosingMacro, r); + } + } + } + } + } + } + for(Map.Entry entry : macros.entrySet()) { + for(CsmReference r : repository.getReferences(entry.getKey(), project, CsmReferenceKind.ANY_REFERENCE_IN_ACTIVE_CODE, Interrupter.DUMMY)){ + if (r == null) { + continue; + } + if (CsmReferenceResolver.getDefault().isKindOf(r,kinds)) { + CsmFunction o = getFunctionDeclaration(getEnclosingFunction(r)); + if (o != null) { + if (!set.containsKey(o)) { + set.put(o, r); + } + } + } + } + } + + CsmOffsetableDeclaration owner; + if (declaration instanceof FunctionImpl) { + FunctionImpl functionImpl = (FunctionImpl) declaration; + owner = functionImpl.getDeclaration(); + } else if (declaration instanceof VariableImpl) { + owner = ((VariableImpl) declaration).getVariable(); + } else { + owner = null; + } + + final CsmOffsetableDeclaration ownerDeclaration = owner; + if (ownerDeclaration != null) { + for(Map.Entry r : set.entrySet()){ + res.add(new GeneralCallImpl(r.getKey(), r.getValue(), ownerDeclaration, true)); + } + } + return res; + } finally { + CsmCacheManager.leave(); + } + } + + @Override + public List getCallees(Function definition) { + CsmCacheManager.enter(); + try { + if (definition instanceof VariableImpl) { + return new ArrayList(); + } else if (definition instanceof FunctionImpl) { + FunctionImpl definitionImpl = (FunctionImpl) definition; + CsmFunction owner = definitionImpl.getDefinition(); + Collection functions = new ArrayList(); + functions.add(owner); + if (CallGraphPreferences.isShowOverriding()) { + if (CsmKindUtilities.isMethodDeclaration(owner)) { + Collection overrides = CsmVirtualInfoQuery.getDefault().getOverriddenMethods((CsmMethod) owner, false); + functions.addAll(overrides); + } + } + List res = new ArrayList(); + final HashMap set = new HashMap(); + for(CsmFunction function : functions) { + if (CsmKindUtilities.isFunctionDefinition(function) && function.getContainingFile().isValid()) { + final List list = CsmFileInfoQuery.getDefault().getUnusedCodeBlocks((function).getContainingFile(), Interrupter.DUMMY); + fileReference.accept((CsmScope)function, null, new CsmFileReferences.Visitor() { + @Override + public void visit(CsmReferenceContext context) { + CsmReference r = context.getReference(); + if (r == null) { + return; + } + for(CsmOffsetable offset:list){ + if (offset.getStartOffset()<=r.getStartOffset() && + offset.getEndOffset() >=r.getEndOffset()){ + return; + } + } + try { + CsmObject o = r.getReferencedObject(); + if (CsmKindUtilities.isFunction(o) && + !CsmReferenceResolver.getDefault().isKindOf(r, CsmReferenceKind.FUNCTION_DECLARATION_KINDS)) { + o = getFunctionDeclaration((CsmFunction)o); + if (!set.containsKey((CsmFunction)o)) { + set.put((CsmFunction)o, r); + } + } else if (CsmKindUtilities.isVariable(o)) { + if (!set.containsKey((CsmVariable) o)) { + set.put((CsmVariable)o, r); + } + } + } catch (AssertionError e){ + e.printStackTrace(System.err); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + @Override + public boolean cancelled() { + return false; + } + }, CsmReferenceKind.ANY_REFERENCE_IN_ACTIVE_CODE); + } + } + final CsmFunction functionDeclaration = getFunctionDeclaration(owner); + if (functionDeclaration != null) { + for(Map.Entry r : set.entrySet()){ + res.add(new GeneralCallImpl(functionDeclaration, r.getValue(),r.getKey(), false)); + } + } + return res; + } else { + return new ArrayList(); + } + } finally { + CsmCacheManager.leave(); + } + } + + private CsmFunction getFunctionDeclaration(CsmFunction definition){ + if (definition != null) { + if (CsmKindUtilities.isFunctionDefinition(definition)) { + return ((CsmFunctionDefinition)definition).getDeclaration(); + } + } + return definition; + } + + private CsmMacro getEnclosingMacro(CsmReference ref){ + CsmObject o = ref.getClosestTopLevelObject(); + if (CsmKindUtilities.isMacro(o)) { + return (CsmMacro) o; + } + return null; + } + + private CsmFunction getEnclosingFunction(CsmReference ref){ + CsmObject o = ref.getClosestTopLevelObject(); + if (CsmKindUtilities.isFunction(o)) { + return (CsmFunction) o; + } + return null; + } + + private static class DeclarationUIN { + private final CsmProject project; + private final CharSequence declarationUin; + private final CsmUID fileUid; + private final CsmOffsetableDeclaration declaration; + + private DeclarationUIN(CsmProject project, CsmOffsetableDeclaration declaration) { + this.project = project; + this.declaration = declaration; + fileUid = UIDs.get(declaration.getContainingFile()); + declarationUin = declaration.getUniqueName(); + } + + private CsmOffsetableDeclaration getDeclaration() { + if (!project.isValid()) { + return null; + } + CsmOffsetableDeclaration root = (CsmOffsetableDeclaration) project.findDeclaration(declarationUin); + if (root != null) { + return root; + } + CsmFile file = fileUid.getObject(); + if (!file.isValid()) { + return null; + } + for (CsmDeclaration d : file.getDeclarations()) { + root = findDeclaration(d); + if (root != null){ + return root; + } + } + return null; + } + + private CsmOffsetableDeclaration findDeclaration(CsmDeclaration element) { + if (CsmKindUtilities.isTypedef(element) || CsmKindUtilities.isTypeAlias(element)) { + CsmTypedef def = (CsmTypedef) element; + if (def.isTypeUnnamed()) { + CsmClassifier cls = def.getType().getClassifier(); + if (cls != null && cls.getName().length() == 0 && + (cls instanceof CsmCompoundClassifier)) { + return findDeclaration((CsmCompoundClassifier) cls); + } + } + return null; + } else if (CsmKindUtilities.isClassifier(element)) { + String name = ((CsmClassifier) element).getName().toString(); + if (name.length() == 0 && (element instanceof CsmCompoundClassifier)) { + Collection list = ((CsmCompoundClassifier) element).getEnclosingTypedefs(); + if (list.size() > 0) { + return null; + } + } + if (CsmKindUtilities.isClass(element)) { + CsmClass cls = (CsmClass) element; + for (CsmMember member : cls.getMembers()) { + CsmOffsetableDeclaration res = findDeclaration(member); + if (res != null) { + return res; + } + } + for (CsmFriend friend : cls.getFriends()) { + CsmOffsetableDeclaration res = findDeclaration(friend); + if (res != null) { + return res; + } + } + } + return null; + } else if (CsmKindUtilities.isNamespaceDefinition(element)) { + for (CsmDeclaration d : ((CsmNamespaceDefinition) element).getDeclarations()) { + CsmOffsetableDeclaration res = findDeclaration(d); + if (res != null) { + return res; + } + } + } else if (CsmKindUtilities.isOffsetableDeclaration(element)) { + final ArrayList list = new ArrayList(); + CsmFileReferences fr = CsmFileReferences.getDefault(); + fr.accept((CsmScope)element.getScope(), null, new CsmFileReferences.Visitor() { + @Override + public void visit(CsmReferenceContext context) { + CsmReference r = context.getReference(); + if (r == null) { + return; + } + try { + CsmObject obj = r.getReferencedObject(); + if (CsmKindUtilities.isVariable(obj)) { + CsmVariable var = (CsmVariable) obj; + if (var.getUniqueName().equals(declarationUin)) { + list.add(var); + } + } + } catch (AssertionError e){ + e.printStackTrace(System.err); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + @Override + public boolean cancelled() { + return false; + } + }, CsmReferenceKind.ANY_REFERENCE_IN_ACTIVE_CODE); + if (!list.isEmpty()) { + return (CsmOffsetableDeclaration) list.get(0); + } + } + return null; + } + } + + private static Function implementationResolver(CsmOffsetableDeclaration entity) { + if (CsmKindUtilities.isFunction(entity)) { + return new FunctionImpl((CsmFunction) entity); + } else if (CsmKindUtilities.isVariable(entity)) { + return new VariableImpl((CsmVariable) entity); + } else { + return null; + } + } +} diff --git a/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/VariableImpl.java b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/VariableImpl.java new file mode 100644 --- /dev/null +++ b/cnd.navigation/src/org/netbeans/modules/cnd/navigation/callgraph/VariableImpl.java @@ -0,0 +1,197 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.navigation.callgraph; + +import java.awt.Image; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.modules.cnd.api.model.CsmVariable; +import org.netbeans.modules.cnd.api.model.CsmClass; +import org.netbeans.modules.cnd.api.model.CsmDeclaration; +import org.netbeans.modules.cnd.api.model.CsmField; +import org.netbeans.modules.cnd.api.model.CsmModelAccessor; +import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities; +import org.netbeans.modules.cnd.callgraph.api.Function; +import org.netbeans.modules.cnd.modelutil.CsmDisplayUtilities; +import org.netbeans.modules.cnd.modelutil.CsmImageLoader; +import org.netbeans.modules.cnd.modelutil.CsmUtilities; +import org.openide.util.NbBundle; + +/** + * + * @author toor + */ +public class VariableImpl implements Function { + private static final Map preferredIcons = new HashMap(); + + static { + preferredIcons.put(CsmDeclaration.Kind.VARIABLE, CsmDeclaration.Kind.VARIABLE_DEFINITION); + } + + private final CsmVariable variable; + private String htmlDisplayName = ""; // NOI18N + private String scopeName = null; // NOI18N + + public VariableImpl(CsmVariable variable) { + this.variable = variable; + } + + @Override + public String getName() { + return variable.getName().toString(); + } + + @Override + public String getScopeName() { + if (scopeName == null) { + scopeName = ""; + try { + if (CsmKindUtilities.isField(variable)) { + CsmClass cls = ((CsmField) variable).getContainingClass(); + if (cls != null && cls.getName().length() > 0) { + scopeName = cls.getName().toString()+"::"; // NOI18N + } + } + } catch (AssertionError ex) { + ex.printStackTrace(System.err); + } catch (Exception ex) { + ex.printStackTrace(System.err); + } + } + return getTypeText() + scopeName; + } + + public CsmVariable getVariable() { + return variable; + } + + @Override + public String getHtmlDisplayName() { + if (htmlDisplayName.length() == 0) { + htmlDisplayName = createHtmlDisplayName(); + } + return htmlDisplayName; + } + + private String createHtmlDisplayName() { + String displayName = getTypeText() + variable.getName().toString(); + + try { + if (CsmKindUtilities.isField(variable)) { + CsmClass cls = ((CsmField) variable).getContainingClass(); + if (cls != null && cls.getName().length() > 0) { + String name = CsmDisplayUtilities.htmlize(cls.getName().toString()); + String in = NbBundle.getMessage(CallImpl.class, "LBL_inClass"); // NOI18N + return displayName + " " + in + " " + name; // NOI18N + } + } + } catch (AssertionError ex) { + ex.printStackTrace(System.err); + } catch (Exception ex) { + ex.printStackTrace(System.err); + } + + return displayName; + } + + private String getTypeText() { + return variable.getType().getClassifierText() + " "; + } + + @Override + public String getDescription() { + return getScopeName()+getName(); + } + + @Override + public Image getIcon() { + try { + return CsmImageLoader.getImage(variable, preferredIcons); + } catch (AssertionError ex) { + ex.printStackTrace(System.err); + } catch (Exception ex) { + ex.printStackTrace(System.err); + } + return null; + } + + @Override + public void open() { + final String taskName = "Open declaration"; //NOI18N + Runnable run = new Runnable() { + + @Override + public void run() { + CsmUtilities.openSource(variable); + } + }; + CsmModelAccessor.getModel().enqueue(run, taskName); + } + + @Override + public boolean equals(Object obj) { + if (variable != null) { + if (obj instanceof VariableImpl) { + return variable.equals(((VariableImpl) obj).getVariable()); + } + } + return super.equals(obj); + } + + @Override + public int hashCode() { + if (variable != null) { + return variable.hashCode(); + } + return super.hashCode(); + } + + @Override + public String toString() { + return getName(); + } + + @Override + public boolean isVurtual() { + return false; + } +}