blob: 560d5cb499c7fcf8ea9956c2bfd28707305af8f3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* QNX Software Systems
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
import static org.eclipse.cdt.core.index.IndexLocationFactory.getAbsolutePath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import com.ibm.icu.text.Collator;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexMacro;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.IFunctionSummary;
import org.eclipse.cdt.ui.IRequiredInclude;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.IASTInternalScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
import org.eclipse.cdt.internal.core.model.ASTStringUtil;
import org.eclipse.cdt.internal.core.util.TextUtil;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.ui.CHelpProviderManager;
/**
* Adds an include statement and, optionally, a 'using' declaration for the currently
* selected name.
*/
public class IncludeCreator {
private static final Collator COLLATOR = Collator.getInstance();
private final IElementSelector fAmbiguityResolver;
private final IncludeCreationContext fContext;
public IncludeCreator(ITranslationUnit tu, IIndex index, IElementSelector ambiguityResolver) {
fAmbiguityResolver = ambiguityResolver;
fContext = new IncludeCreationContext(tu, index);
}
public MultiTextEdit createInclude(IASTTranslationUnit ast, ITextSelection selection)
throws CoreException {
MultiTextEdit rootEdit = new MultiTextEdit();
ITranslationUnit tu = fContext.getTranslationUnit();
IASTNodeSelector selector = ast.getNodeSelector(tu.getLocation().toOSString());
IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength());
if (name == null) {
return rootEdit;
}
char[] nameChars = name.toCharArray();
String lookupName = new String(nameChars);
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPVariable) {
IType type = ((ICPPVariable) binding).getType();
type = SemanticUtil.getNestedType(type,
SemanticUtil.ALLCVQ | SemanticUtil.PTR | SemanticUtil.ARRAY | SemanticUtil.REF);
if (type instanceof IBinding) {
binding = (IBinding) type;
nameChars = binding.getNameCharArray();
}
}
if (nameChars.length == 0) {
return rootEdit;
}
final Map<String, IncludeCandidate> candidatesMap= new HashMap<>();
final IndexFilter filter = IndexFilter.getDeclaredBindingFilter(ast.getLinkage().getLinkageID(), false);
final List<IncludeInfo> requiredIncludes = new ArrayList<>();
final List<UsingDeclaration> usingDeclarations = new ArrayList<>();
List<IIndexBinding> bindings = new ArrayList<>();
try {
IIndex index = fContext.getIndex();
IIndexBinding adaptedBinding= index.adaptBinding(binding);
if (adaptedBinding == null) {
bindings.addAll(Arrays.asList(index.findBindings(nameChars, false, filter, new NullProgressMonitor())));
} else {
bindings.add(adaptedBinding);
while (adaptedBinding instanceof ICPPSpecialization) {
adaptedBinding= index.adaptBinding(((ICPPSpecialization) adaptedBinding).getSpecializedBinding());
if (adaptedBinding != null) {
bindings.add(adaptedBinding);
}
}
}
HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext);
for (IIndexBinding indexBinding : bindings) {
// Replace ctor with the class itself.
if (indexBinding instanceof ICPPConstructor) {
indexBinding = indexBinding.getOwner();
}
IIndexName[] definitions= null;
// class, struct, union, enum-type, enum-item
if (indexBinding instanceof ICompositeType || indexBinding instanceof IEnumeration
|| indexBinding instanceof IEnumerator) {
definitions= index.findDefinitions(indexBinding);
} else if (indexBinding instanceof ITypedef || indexBinding instanceof IFunction) {
definitions = index.findDeclarations(indexBinding);
}
if (definitions != null) {
for (IIndexName definition : definitions) {
considerForInclusion(ast, definition, indexBinding, index, headerSubstitutor,
candidatesMap);
}
if (definitions.length > 0 && adaptedBinding != null)
break;
}
}
IIndexMacro[] macros = index.findMacros(nameChars, filter, new NullProgressMonitor());
for (IIndexMacro macro : macros) {
IIndexName definition = macro.getDefinition();
considerForInclusion(ast, definition, macro, index, headerSubstitutor, candidatesMap);
}
final ArrayList<IncludeCandidate> candidates = new ArrayList<>(candidatesMap.values());
if (candidates.size() > 1) {
// First, try to resolve the ambiguity by comparing the namespaces of the
// candidate bindings to the namespace in which the source name occurs.
IncludeCandidate candidate = selectCandidateByNamespace(name, candidates);
// If that doesn't disambiguate, fall back to the ambiguity resolver
// provided by the user of this class.
if (candidate == null)
candidate = fAmbiguityResolver.selectElement(candidates);
if (candidate == null)
return rootEdit;
candidates.clear();
candidates.add(candidate);
}
if (candidates.size() == 1) {
IncludeCandidate candidate = candidates.get(0);
requiredIncludes.add(candidate.include);
IIndexBinding indexBinding = candidate.binding;
if (indexBinding instanceof ICPPBinding && !(indexBinding instanceof IIndexMacro)) {
// Decide what 'using' declaration, if any, should be added along with the include.
UsingDeclaration usingDeclaration = deduceUsingDeclaration(binding, indexBinding, ast);
if (usingDeclaration != null)
usingDeclarations.add(usingDeclaration);
}
}
} catch (CoreException e) {
CUIPlugin.log(e);
return rootEdit;
}
if (requiredIncludes.isEmpty() && !lookupName.isEmpty()) {
// Try contribution from plug-ins.
IFunctionSummary fs = findContribution(lookupName);
if (fs != null) {
IRequiredInclude[] functionIncludes = fs.getIncludes();
if (functionIncludes != null) {
for (IRequiredInclude include : functionIncludes) {
requiredIncludes.add(new IncludeInfo(include.getIncludeName(), include.isStandard()));
}
}
String ns = fs.getNamespace();
if (ns != null && !ns.isEmpty()) {
usingDeclarations.add(new UsingDeclaration(ns + "::" + fs.getName())); //$NON-NLS-1$
}
}
}
return createEdit(requiredIncludes, usingDeclarations, ast, selection);
}
private ICPPNamespaceScope getContainingNamespaceScope(IASTNode node) {
IScope scope = CPPVisitor.getContainingScope(node);
while (scope != null) {
if (scope instanceof ICPPNamespaceScope && !(scope instanceof ICPPBlockScope)) {
return (ICPPNamespaceScope) scope;
}
try {
scope = scope.getParent();
} catch (DOMException e) {
return null;
}
}
return null;
}
private ICPPNamespace getContainingNamespace(IASTName name) {
ICPPNamespaceScope scope = getContainingNamespaceScope(name);
// TODO(nathanridge): Move this ICPPNamespaceScope -> ICPPNamespace
// mapping code to a utility class.
if (scope instanceof ICPPNamespace) {
return (ICPPNamespace) scope;
}
if (scope instanceof IASTInternalScope) {
IASTNode node = ((IASTInternalScope) scope).getPhysicalNode();
if (node instanceof ICPPASTNamespaceDefinition) {
IBinding namespace = ((ICPPASTNamespaceDefinition) node).getName().resolveBinding();
if (namespace instanceof ICPPNamespace) {
return (ICPPNamespace) namespace;
}
}
}
return null;
}
private ICPPNamespace getContainingNamespace(IBinding binding) {
while (binding != null) {
if (binding instanceof ICPPNamespace) {
return (ICPPNamespace) binding;
}
binding = binding.getOwner();
}
return null;
}
private IncludeCandidate selectCandidateByNamespace(IASTName sourceName, ArrayList<IncludeCandidate> candidates) {
// If one of the candidates is in the same namespace as the source name,
// and the others aren't, prefer that candidate.
ICPPNamespace sourceNamespace = getContainingNamespace(sourceName);
if (sourceNamespace == null) {
return null;
}
IncludeCandidate winner = null;
for (IncludeCandidate candidate : candidates) {
if (SemanticUtil.isSameNamespace(sourceNamespace, getContainingNamespace(candidate.binding))) {
if (winner != null) {
return null; // ambiguous between 'winner' and 'candidate'
}
winner = candidate;
}
}
return winner;
}
private MultiTextEdit createEdit(List<IncludeInfo> includes,
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
ITextSelection selection) {
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
String contents = fContext.getSourceContents();
IRegion includeRegion =
IncludeUtil.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
IncludePreferences preferences = fContext.getPreferences();
MultiTextEdit rootEdit = new MultiTextEdit();
IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives();
fContext.addHeadersIncludedPreviously(existingIncludes);
List<StyledInclude> styledIncludes = new ArrayList<>();
// Put the new includes into styledIncludes.
for (IncludeInfo includeInfo : includes) {
IPath header = fContext.resolveInclude(includeInfo);
if (header != null && !fContext.wasIncludedPreviously(header)) {
IncludeGroupStyle style = fContext.getIncludeStyle(includeInfo);
StyledInclude prototype = new StyledInclude(header, includeInfo, style);
styledIncludes.add(prototype);
}
}
Collections.sort(styledIncludes, preferences);
List<StyledInclude> mergedIncludes =
IncludeUtil.getIncludesInRegion(existingIncludes, includeRegion, fContext);
if (preferences.allowReordering) {
// Since the order of existing include statements may not match the include order
// preferences, we find positions for the new include statements by pushing them up
// from the bottom of the include insertion region.
for (StyledInclude include : styledIncludes) {
int i = mergedIncludes.size();
while (--i >= 0 && preferences.compare(include, mergedIncludes.get(i)) < 0) {}
mergedIncludes.add(i + 1, include);
}
} else {
mergedIncludes.addAll(styledIncludes);
}
int offset = includeRegion.getOffset();
StringBuilder text = new StringBuilder();
StyledInclude previousInclude = null;
for (StyledInclude include : mergedIncludes) {
IASTPreprocessorIncludeStatement existingInclude = include.getExistingInclude();
if (existingInclude == null) {
if (previousInclude != null) {
IASTNode previousNode = previousInclude.getExistingInclude();
if (previousNode != null) {
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
flushEditBuffer(offset, text, rootEdit);
if (contents.charAt(offset - 1) != '\n')
text.append(fContext.getLineDelimiter());
}
if (include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles)) {
if (TextUtil.isLineBlank(contents, offset)) {
offset = TextUtil.skipToNextLine(contents, offset);
} else {
text.append(fContext.getLineDelimiter());
}
}
}
text.append(include.getIncludeInfo().composeIncludeStatement());
text.append(fContext.getLineDelimiter());
} else {
if (previousInclude != null && previousInclude.getExistingInclude() == null &&
include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles) &&
!TextUtil.isPreviousLineBlank(contents, ASTNodes.offset(existingInclude))) {
text.append(fContext.getLineDelimiter());
}
flushEditBuffer(offset, text, rootEdit);
}
previousInclude = include;
}
if (includeRegion.getLength() == 0 && !TextUtil.isLineBlank(contents, includeRegion.getOffset()) &&
!includes.isEmpty()) {
text.append(fContext.getLineDelimiter());
}
flushEditBuffer(offset, text, rootEdit);
List<UsingDeclaration> mergedUsingDeclarations = getUsingDeclarations(ast);
for (UsingDeclaration usingDeclaration : mergedUsingDeclarations) {
for (Iterator<UsingDeclaration> iter = usingDeclarations.iterator(); iter.hasNext();) {
UsingDeclaration using = iter.next();
if (using.equals(usingDeclaration.name))
iter.remove();
}
}
if (usingDeclarations.isEmpty())
return rootEdit;
List<UsingDeclaration> temp = null;
for (Iterator<UsingDeclaration> iter = mergedUsingDeclarations.iterator(); iter.hasNext();) {
UsingDeclaration usingDeclaration = iter.next();
if (usingDeclaration.existingDeclaration.isPartOfTranslationUnitFile() &&
ASTNodes.endOffset(usingDeclaration.existingDeclaration) <= selection.getOffset()) {
if (temp == null)
temp = new ArrayList<>();
temp.add(usingDeclaration);
}
}
if (temp == null) {
mergedUsingDeclarations.clear();
} else {
mergedUsingDeclarations = temp;
}
Collections.sort(usingDeclarations);
if (mergedUsingDeclarations.isEmpty()) {
offset = includeRegion.getOffset() + includeRegion.getLength();
text.append(fContext.getLineDelimiter()); // Blank line between includes and using declarations.
} else {
offset = commentedNodeMap.getOffsetIncludingComments(mergedUsingDeclarations.get(0).existingDeclaration);
}
// Since the order of existing using declarations may not be alphabetical, we find positions
// for the new using declarations by pushing them from them up from the bottom of
// the using declaration list.
for (UsingDeclaration using : usingDeclarations) {
int i = mergedUsingDeclarations.size();
while (--i >= 0 && using.compareTo(mergedUsingDeclarations.get(i)) < 0) {}
mergedUsingDeclarations.add(i + 1, using);
}
UsingDeclaration previousUsing = null;
for (UsingDeclaration using : mergedUsingDeclarations) {
if (using.existingDeclaration == null) {
if (previousUsing != null) {
IASTNode previousNode = previousUsing.existingDeclaration;
if (previousNode != null) {
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
flushEditBuffer(offset, text, rootEdit);
if (contents.charAt(offset - 1) != '\n')
text.append(fContext.getLineDelimiter());
}
}
text.append(using.composeDirective());
text.append(fContext.getLineDelimiter());
} else {
flushEditBuffer(offset, text, rootEdit);
}
previousUsing = using;
}
flushEditBuffer(offset, text, rootEdit);
return rootEdit;
}
private List<UsingDeclaration> getUsingDeclarations(IASTTranslationUnit ast) {
List<UsingDeclaration> usingDeclarations = new ArrayList<>();
IASTDeclaration[] declarations = ast.getDeclarations();
for (IASTDeclaration declaration : declarations) {
if (declaration instanceof ICPPASTUsingDeclaration) {
usingDeclarations.add(new UsingDeclaration((ICPPASTUsingDeclaration) declaration));
}
}
return usingDeclarations;
}
private void flushEditBuffer(int offset, StringBuilder text, MultiTextEdit edit) {
if (text.length() != 0) {
edit.addChild(new InsertEdit(offset, text.toString()));
text.delete(0, text.length());
}
}
/**
* Adds an include candidate to the {@code candidates} map if the file containing the definition
* is suitable for inclusion.
*/
private void considerForInclusion(IASTTranslationUnit ast, IIndexName definition, IIndexBinding binding,
IIndex index, HeaderSubstitutor headerSubstitutor, Map<String, IncludeCandidate> candidates)
throws CoreException {
if (definition == null) {
return;
}
IIndexFile file = definition.getFile();
// Consider the file for inclusion only if it is not a source file,
// or a source file that was already included by some other file.
if (fContext.canBeIncluded(file)) {
IncludeInfo include;
IPath header = getAbsolutePath(file.getLocation());
header = headerSubstitutor.getPreferredRepresentativeHeader(header);
if (fContext.getPreferences().heuristicHeaderSubstitution) {
boolean reachable = ast.getIndexFileSet().contains(file);
InclusionRequest request =
new InclusionRequest(binding, Collections.singletonMap(file, header), reachable);
header = headerSubstitutor.getPreferredRepresentativeHeaderByHeuristic(request);
}
IncludeGroupStyle style = fContext.getIncludeStyle(header);
include = fContext.createIncludeInfo(header, style);
if (include != null) {
IncludeCandidate candidate = new IncludeCandidate(binding, include);
if (!candidates.containsKey(candidate.toString())) {
candidates.put(candidate.toString(), candidate);
}
}
}
}
private UsingDeclaration deduceUsingDeclaration(IBinding source, IBinding target,
IASTTranslationUnit ast) {
if (source.equals(target)) {
return null; // No using declaration is needed.
}
ArrayList<String> targetChain = getUsingChain(target);
if (targetChain.size() <= 1) {
return null; // Target is not in a namespace.
}
// Check if any of the existing using declarations and directives matches the target.
final IASTDeclaration[] declarations= ast.getDeclarations(false);
for (IASTDeclaration declaration : declarations) {
if (declaration.isPartOfTranslationUnitFile()) {
IASTName name = null;
if (declaration instanceof ICPPASTUsingDeclaration) {
name = ((ICPPASTUsingDeclaration) declaration).getName();
if (match(name, targetChain, false)) {
return null;
}
} else if (declaration instanceof ICPPASTUsingDirective) {
name = ((ICPPASTUsingDirective) declaration).getQualifiedName();
if (match(name, targetChain, true)) {
return null;
}
}
}
}
ArrayList<String> sourceChain = getUsingChain(source);
if (sourceChain.size() >= targetChain.size()) {
int j = targetChain.size();
for (int i = sourceChain.size(); --j >= 1 && --i >= 1;) {
if (!sourceChain.get(i).equals(targetChain.get(j))) {
break;
}
}
if (j <= 0) {
return null; // Source is in the target's namespace
}
}
StringBuilder buf = new StringBuilder();
for (int i = targetChain.size(); --i >= 0;) {
if (buf.length() > 0) {
buf.append("::"); //$NON-NLS-1$
}
buf.append(targetChain.get(i));
}
return new UsingDeclaration(buf.toString());
}
private boolean match(IASTName name, ArrayList<String> usingChain, boolean excludeLast) {
ICPPASTNameSpecifier[] qualifiers;
if (name instanceof ICPPASTQualifiedName) {
qualifiers = ((ICPPASTQualifiedName) name).getQualifier();
} else {
qualifiers = ICPPASTNameSpecifier.EMPTY_NAME_SPECIFIER_ARRAY;
}
if (qualifiers.length + 1 != usingChain.size() - (excludeLast ? 1 : 0))
return false;
for (int i = 0; i < qualifiers.length; i++) {
if (!qualifiers[i].toString().equals(usingChain.get(usingChain.size() - 1 - i)))
return false;
}
return name.getLastName().toString().equals(usingChain.get(usingChain.size() - 1 - qualifiers.length));
}
private ArrayList<String> getUsingChainForProblemBinding(IProblemBinding binding) {
ArrayList<String> chain = new ArrayList<>(4);
chain.add(binding.getName());
IASTNode node = binding.getASTNode();
if (node.getParent() instanceof ICPPASTQualifiedName) {
// If the ProblemBinding is for a name inside the qualified name,
// use the chain of preceding segments in the qualifier as the
// using chain.
ICPPASTQualifiedName qualifiedName = (ICPPASTQualifiedName) node.getParent();
ICPPASTNameSpecifier[] qualifier = qualifiedName.getQualifier();
int i = qualifier.length;
if (node != qualifiedName.getLastName()) {
while (--i >= 0) {
if (qualifier[i] == node) {
break;
}
}
}
while (--i >= 0) {
chain.add(qualifier[i].resolveBinding().getName());
}
} else {
// Otherwise, fall back to the chain of namespaces that physically
// contain the name.
ICPPNamespaceScope namespace = getContainingNamespaceScope(node);
while (namespace != null) {
IName namespaceName = namespace.getScopeName();
if (namespaceName != null) {
chain.add(new String(namespaceName.getSimpleID()));
}
try {
IScope parent = namespace.getParent();
if (parent instanceof ICPPNamespaceScope) {
namespace = (ICPPNamespaceScope) parent;
} else {
break;
}
} catch (DOMException e) {
break;
}
}
}
return chain;
}
/**
* Returns components of the qualified name in reverse order.
* For ns1::ns2::Name, e.g., it returns [Name, ns2, ns1].
*/
private ArrayList<String> getUsingChain(IBinding binding) {
// For ProblemBindings, getOwner() is very heuristic and doesn't
// produce the chain of owner bindings we want here, so we
// handle it specially.
if (binding instanceof IProblemBinding) {
return getUsingChainForProblemBinding((IProblemBinding) binding);
}
ArrayList<String> chain = new ArrayList<>(4);
for (; binding != null; binding = binding.getOwner()) {
String name = binding.getName();
if (binding instanceof ICPPNamespace) {
if (name.length() == 0) {
continue;
}
} else {
chain.clear();
}
chain.add(name);
}
return chain;
}
private IFunctionSummary findContribution(final String name) throws CoreException {
ICHelpInvocationContext context = new ICHelpInvocationContext() {
@Override
public IProject getProject() {
return fContext.getProject();
}
@Override
public ITranslationUnit getTranslationUnit() {
return fContext.getTranslationUnit();
}
};
return CHelpProviderManager.getDefault().getFunctionInfo(context, name);
}
/**
* Returns the fully qualified name for a given index binding.
*
* @param binding
* @return binding's fully qualified name
* @throws CoreException
*/
private static String getBindingQualifiedName(IIndexBinding binding) throws CoreException {
String[] qname= CPPVisitor.getQualifiedName(binding);
StringBuilder result = new StringBuilder();
boolean needSep= false;
for (String element : qname) {
if (needSep)
result.append(Keywords.cpCOLONCOLON);
result.append(element);
needSep= true;
}
return result.toString();
}
/**
* To be used by ElementListSelectionDialog for user to choose which declarations/
* definitions for "add include" when there are more than one to choose from.
*/
private static class IncludeCandidate {
final IIndexBinding binding;
final IncludeInfo include;
final String label;
IncludeCandidate(IIndexBinding binding, IncludeInfo include) throws CoreException {
this.binding = binding;
this.include = include;
this.label = getBindingQualifiedName(binding) + " - " + include.toString(); //$NON-NLS-1$
}
@Override
public String toString() {
return label;
}
}
private static class UsingDeclaration implements Comparable<UsingDeclaration> {
final String name;
final ICPPASTUsingDeclaration existingDeclaration;
UsingDeclaration(String name) {
this.name = name;
this.existingDeclaration = null;
}
UsingDeclaration(ICPPASTUsingDeclaration existingDeclaration) {
this.name = ASTStringUtil.getQualifiedName(existingDeclaration.getName());
this.existingDeclaration = existingDeclaration;
}
@Override
public int compareTo(UsingDeclaration other) {
return COLLATOR.compare(name, other.name);
}
String composeDirective() {
return "using " + name + ';'; //$NON-NLS-1$
}
}
}