/*******************************************************************************
* Copyright (c) 2007, 2010 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
* Ed Swartz (Nokia)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.viewsupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IEditorInput;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IPositionConverter;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
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.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
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.IField;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
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.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
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.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IInclude;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ISourceRange;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInstanceCache;
import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
import org.eclipse.cdt.internal.core.model.ext.CElementHandleFactory;
import org.eclipse.cdt.internal.core.model.ext.ICElementHandle;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.cdt.internal.ui.editor.ASTProvider;
public class IndexUI {
private static final ICElementHandle[] EMPTY_ELEMENTS = new ICElementHandle[0];
public static IIndexBinding elementToBinding(IIndex index, ICElement element) throws CoreException {
return elementToBinding(index, element, -1);
}
public static IIndexBinding elementToBinding(IIndex index, ICElement element, int linkageID) throws CoreException {
if (element instanceof ISourceReference) {
ISourceReference sf = ((ISourceReference)element);
ISourceRange range= sf.getSourceRange();
if (range.getIdLength() != 0) {
IIndexName name= elementToName(index, element, linkageID);
if (name != null) {
return index.findBinding(name);
}
} else {
String name= element.getElementName();
name= name.substring(name.lastIndexOf(':')+1);
IIndexBinding[] bindings= index.findBindings(name.toCharArray(), IndexFilter.ALL, new NullProgressMonitor());
for (IIndexBinding binding : bindings) {
if (checkBinding(binding, element)) {
return binding;
}
}
}
}
return null;
}
private static boolean checkBinding(IIndexBinding binding, ICElement element) {
switch(element.getElementType()) {
case ICElement.C_ENUMERATION:
return binding instanceof IEnumeration;
case ICElement.C_NAMESPACE:
return binding instanceof ICPPNamespace;
case ICElement.C_STRUCT_DECLARATION:
case ICElement.C_STRUCT:
return binding instanceof ICompositeType &&
((ICompositeType) binding).getKey() == ICompositeType.k_struct;
case ICElement.C_CLASS:
case ICElement.C_CLASS_DECLARATION:
return binding instanceof ICPPClassType &&
((ICompositeType) binding).getKey() == ICPPClassType.k_class;
case ICElement.C_UNION:
case ICElement.C_UNION_DECLARATION:
return binding instanceof ICompositeType &&
((ICompositeType) binding).getKey() == ICompositeType.k_union;
case ICElement.C_TYPEDEF:
return binding instanceof ITypedef;
case ICElement.C_METHOD:
case ICElement.C_METHOD_DECLARATION:
return binding instanceof ICPPMethod;
case ICElement.C_FIELD:
return binding instanceof IField;
case ICElement.C_FUNCTION:
case ICElement.C_FUNCTION_DECLARATION:
return binding instanceof ICPPFunction && !(binding instanceof ICPPMethod);
case ICElement.C_VARIABLE:
case ICElement.C_VARIABLE_DECLARATION:
return binding instanceof IVariable;
case ICElement.C_ENUMERATOR:
return binding instanceof IEnumerator;
case ICElement.C_TEMPLATE_CLASS:
case ICElement.C_TEMPLATE_CLASS_DECLARATION:
case ICElement.C_TEMPLATE_STRUCT:
case ICElement.C_TEMPLATE_STRUCT_DECLARATION:
case ICElement.C_TEMPLATE_UNION:
case ICElement.C_TEMPLATE_UNION_DECLARATION:
return binding instanceof ICPPClassTemplate;
case ICElement.C_TEMPLATE_FUNCTION:
case ICElement.C_TEMPLATE_FUNCTION_DECLARATION:
return binding instanceof ICPPFunctionTemplate && !(binding instanceof ICPPMethod);
case ICElement.C_TEMPLATE_METHOD_DECLARATION:
case ICElement.C_TEMPLATE_METHOD:
return binding instanceof ICPPFunctionTemplate && binding instanceof ICPPMethod;
case ICElement.C_TEMPLATE_VARIABLE:
return binding instanceof ICPPTemplateParameter;
}
return false;
}
public static IIndexName elementToName(IIndex index, ICElement element) throws CoreException {
return elementToName(index, element, -1);
}
public static IIndexName elementToName(IIndex index, ICElement element, int linkageID) throws CoreException {
if (element instanceof ISourceReference) {
ISourceReference sf = ((ISourceReference)element);
ITranslationUnit tu= sf.getTranslationUnit();
if (tu != null) {
IIndexFileLocation location= IndexLocationFactory.getIFL(tu);
if (location != null) {
IIndexFile[] files= index.getFiles(location);
for (IIndexFile file : files) {
if (linkageID == -1 || file.getLinkageID() == linkageID) {
String elementName= element.getElementName();
int idx= elementName.lastIndexOf(":") + 1; //$NON-NLS-1$
ISourceRange pos= sf.getSourceRange();
IRegion region = getConvertedRegion(tu, file, pos.getIdStartPos() + idx, pos.getIdLength() - idx);
IIndexName[] names= file.findNames(region.getOffset(), region.getLength());
for (IIndexName name2 : names) {
IIndexName name = name2;
if (!name.isReference() && elementName.endsWith(new String(name.getSimpleID()))) {
return name;
}
}
}
}
}
}
}
return null;
}
public static boolean isIndexed(IIndex index, ICElement element) throws CoreException {
if (element instanceof ISourceReference) {
ISourceReference sf = ((ISourceReference)element);
ITranslationUnit tu= sf.getTranslationUnit();
if (tu != null) {
IIndexFileLocation location= IndexLocationFactory.getIFL(tu);
if (location != null) {
IIndexFile[] files= index.getFiles(location);
return files.length > 0;
}
}
}
return false;
}
private static IRegion getConvertedRegion(ITranslationUnit tu, IIndexFile file, int pos,
int length) throws CoreException {
IRegion region= new Region(pos, length);
IPositionConverter converter=
CCorePlugin.getPositionTrackerManager().findPositionConverter(tu, file.getTimestamp());
if (converter != null) {
region= converter.actualToHistoric(region);
}
return region;
}
public static IIndexInclude elementToInclude(IIndex index, IInclude include) throws CoreException {
if (include != null) {
ITranslationUnit tu= include.getTranslationUnit();
if (tu != null) {
IIndexFileLocation location= IndexLocationFactory.getIFL(tu);
if (location != null) {
IIndexFile[] files= index.getFiles(location);
for (IIndexFile file : files) {
String elementName= include.getElementName();
ISourceRange pos= include.getSourceRange();
IRegion region= getConvertedRegion(tu, file, pos.getIdStartPos(), pos.getIdLength());
IIndexInclude[] includes= index.findIncludes(file);
int bestDiff= Integer.MAX_VALUE;
IIndexInclude best= null;
for (IIndexInclude candidate : includes) {
int diff= Math.abs(candidate.getNameOffset() - region.getOffset());
if (diff > bestDiff) {
break;
}
if (candidate.getFullName().endsWith(elementName)) {
bestDiff= diff;
best= candidate;
}
}
if (best != null)
return best;
}
}
}
}
return null;
}
public static ICElementHandle[] findRepresentative(IIndex index, IBinding binding) throws CoreException {
ICElementHandle[] defs = IndexUI.findAllDefinitions(index, binding);
if (defs.length == 0) {
ICElementHandle elem = IndexUI.findAnyDeclaration(index, null, binding);
if (elem != null) {
defs = new ICElementHandle[] { elem };
}
}
return defs;
}
public static ICElementHandle[] findAllDefinitions(IIndex index, IBinding binding) throws CoreException {
if (binding != null) {
IIndexName[] defs= index.findNames(binding, IIndex.FIND_DEFINITIONS | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
ArrayList<ICElementHandle> result= new ArrayList<ICElementHandle>();
for (IIndexName in : defs) {
ICElementHandle definition= getCElementForName((ICProject) null, index, in);
if (definition != null) {
result.add(definition);
}
}
return result.toArray(new ICElementHandle[result.size()]);
}
return EMPTY_ELEMENTS;
}
/**
* Creates CElementHandles for definitions or declarations when you expect to find those
* in the index.
* @param preferProject
* @param index
* @param declName
* @return the ICElementHandle or <code>null</code>.
*/
public static ICElementHandle getCElementForName(ICProject preferProject, IIndex index, IASTName declName)
throws CoreException {
assert !declName.isReference();
IBinding binding= declName.resolveBinding();
if (binding != null) {
ITranslationUnit tu= getTranslationUnit(preferProject, declName);
if (tu != null) {
IFile file= (IFile) tu.getResource();
long timestamp= file != null ? file.getLocalTimeStamp() : 0;
IASTFileLocation loc= declName.getFileLocation();
IRegion region= new Region(loc.getNodeOffset(), loc.getNodeLength());
IPositionConverter converter= CCorePlugin.getPositionTrackerManager().findPositionConverter(tu, timestamp);
if (converter != null) {
region= converter.actualToHistoric(region);
}
return CElementHandleFactory.create(tu, binding, declName.isDefinition(), region, timestamp);
}
}
return null;
}
public static ITranslationUnit getTranslationUnit(ICProject cproject, IASTName name) {
return getTranslationUnit(cproject, name.getFileLocation());
}
public static ITranslationUnit getTranslationUnit(ICProject cproject, IIndexName name) {
try {
return CoreModelUtil.findTranslationUnitForLocation(name.getFile().getLocation(), cproject);
} catch (CoreException e) {
CUIPlugin.log(e);
}
return null;
}
private static ITranslationUnit getTranslationUnit(ICProject cproject, final IASTFileLocation fileLocation) {
if (fileLocation != null) {
IPath path= Path.fromOSString(fileLocation.getFileName());
try {
return CoreModelUtil.findTranslationUnitForLocation(path, cproject);
} catch (CModelException e) {
CUIPlugin.log(e);
}
}
return null;
}
public static ICElementHandle getCElementForName(ICProject preferProject, IIndex index, IIndexName declName)
throws CoreException {
assert !declName.isReference();
ITranslationUnit tu= getTranslationUnit(preferProject, declName);
if (tu != null) {
return getCElementForName(tu, index, declName);
}
return null;
}
public static ICElementHandle getCElementForName(ITranslationUnit tu, IIndex index, IIndexName declName)
throws CoreException {
IRegion region= new Region(declName.getNodeOffset(), declName.getNodeLength());
long timestamp= declName.getFile().getTimestamp();
return CElementHandleFactory.create(tu, index.findBinding(declName), declName.isDefinition(), region, timestamp);
}
public static ICElementHandle getCElementForMacro(ICProject preferProject, IIndex index, IIndexMacro macro)
throws CoreException {
ITranslationUnit tu= getTranslationUnit(preferProject, macro.getFileLocation());
if (tu != null) {
IIndexName name= macro.getDefinition();
if (name != null) {
IRegion region= new Region(name.getNodeOffset(), name.getNodeLength());
long timestamp= macro.getFile().getTimestamp();
return CElementHandleFactory.create(tu, macro, region, timestamp);
}
}
return null;
}
public static ICElementHandle findAnyDeclaration(IIndex index, ICProject preferProject, IBinding binding)
throws CoreException {
if (binding != null) {
IIndexName[] names= index.findNames(binding, IIndex.FIND_DECLARATIONS);
for (IIndexName name : names) {
ICElementHandle elem= getCElementForName(preferProject, index, name);
if (elem != null) {
return elem;
}
}
}
return null;
}
public static IASTName getSelectedName(IEditorInput editorInput, ITextSelection selection) throws CoreException {
return getSelectedName(editorInput, selection.getOffset(), selection.getLength());
}
public static IASTName getSelectedName(IEditorInput editorInput, IRegion selection) throws CoreException {
return getSelectedName(editorInput, selection.getOffset(), selection.getLength());
}
private static IASTName getSelectedName(IEditorInput editorInput, final int offset, final int length) {
IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editorInput);
if (workingCopy == null)
return null;
final IASTName[] result= {null};
ASTProvider.getASTProvider().runOnAST(workingCopy, ASTProvider.WAIT_ACTIVE_ONLY, null, new ASTRunnable() {
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
if (ast != null) {
final IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
IASTName name= nodeSelector.findEnclosingName(offset, length);
if (name == null) {
name= nodeSelector.findImplicitName(offset, length);
}
if (name != null && name.getParent() instanceof IASTPreprocessorMacroExpansion) {
IASTFileLocation floc= name.getParent().getFileLocation();
IASTNode node= nodeSelector.findEnclosingNodeInExpansion(floc.getNodeOffset(), floc.getNodeLength());
if (node instanceof IASTName) {
name= (IASTName) node;
} else if (node instanceof IASTFunctionCallExpression){
IASTExpression expr= ((IASTFunctionCallExpression) node).getFunctionNameExpression();
if (expr instanceof IASTIdExpression) {
name= ((IASTIdExpression) expr).getName();
}
} else {
if (node instanceof IASTSimpleDeclaration) {
IASTNode[] dtors= ((IASTSimpleDeclaration) node).getDeclarators();
if (dtors != null && dtors.length > 0) {
node= dtors[0];
}
} else if (node instanceof IASTFunctionDefinition) {
node= ((IASTFunctionDefinition) node).getDeclarator();
}
if (node instanceof IASTDeclarator) {
IASTDeclarator dtor= ASTQueries.findTypeRelevantDeclarator((IASTDeclarator) node);
name= dtor.getName();
}
}
}
result[0]= name;
}
return Status.OK_STATUS;
}
});
return result[0];
}
public static String getFileNotIndexedMessage(ICElement input) {
ITranslationUnit tu= null;
if (input instanceof ISourceReference) {
ISourceReference ref= (ISourceReference) input;
tu= ref.getTranslationUnit();
}
if (tu == null) {
return NLS.bind(Messages.IndexUI_infoNotInSource, input.getElementName());
}
String msg= NLS.bind(Messages.IndexUI_infoNotInIndex, tu.getElementName());
IResource res= tu.getResource();
if (res != null) {
Properties props= IndexerPreferences.getProperties(res.getProject());
if (props == null || !"true".equals(props.get(IndexerPreferences.KEY_INDEX_ALL_FILES)) || //$NON-NLS-1$
(!"true".equals(props.get(IndexerPreferences.KEY_INDEX_UNUSED_HEADERS_WITH_DEFAULT_LANG)) && //$NON-NLS-1$
!"true".equals(props.get(IndexerPreferences.KEY_INDEX_UNUSED_HEADERS_WITH_ALTERNATE_LANG)))) { //$NON-NLS-1$
msg= msg + " " + Messages.IndexUI_infoSelectIndexAllFiles; //$NON-NLS-1$
}
}
return msg;
}
public static ICElement attemptConvertionToHandle(IIndex index, ICElement input) throws CoreException {
if (input instanceof ICElementHandle) {
return input;
}
IIndexName name= IndexUI.elementToName(index, input);
if (name != null) {
ICElement handle= getCElementForName(input.getCProject(), index, name);
if (handle != null) {
return handle;
}
}
return input;
}
/**
* Searches for all specializations that depend on the definition of the given binding.
*/
public static List<? extends IBinding> findSpecializations(IBinding binding) throws CoreException {
List<IBinding> result= null;
IBinding owner = binding.getOwner();
if (owner != null) {
List<? extends IBinding> specializedOwners= findSpecializations(owner);
if (!specializedOwners.isEmpty()) {
result= new ArrayList<IBinding>(specializedOwners.size());
for (IBinding specOwner : specializedOwners) {
if (specOwner instanceof ICPPClassSpecialization) {
result.add(((ICPPClassSpecialization) specOwner).specializeMember(binding));
}
}
}
}
if (binding instanceof ICPPInstanceCache) {
final List<ICPPTemplateInstance> instances= Arrays.asList(((ICPPInstanceCache) binding).getAllInstances());
if (!instances.isEmpty()) {
if (result == null)
result= new ArrayList<IBinding>(instances.size());
for (ICPPTemplateInstance inst : instances) {
if (!ASTInternal.hasDeclaration(inst)) {
result.add(inst);
}
}
}
}
if (result != null) {
return result;
}
return Collections.emptyList();
}
}