blob: 84805464d33dff0711ec1e62ab136510b53bedd7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Doug Schaefer (QNX) - Initial API and implementation
* IBM Corporation
* Andrew Ferguson (Symbian)
* Anton Leherbauer (Wind River Systems)
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.core.browser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
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.IIndexMacro;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility;
import org.eclipse.cdt.internal.core.browser.IndexModelUtil;
import org.eclipse.cdt.internal.core.browser.IndexTypeReference;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
/**
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public class IndexTypeInfo implements ITypeInfo, IFunctionInfo {
private static int hashCode(String[] array) {
int prime = 31;
if (array == null)
return 0;
int result = 1;
for (String element : array) {
result = prime * result + (element == null ? 0 : element.hashCode());
}
return result;
}
private final String[] fqn;
private final IIndexFileLocation fileLocal;
private final int elementType;
private final IIndex index;
private final String[] params;
private final String returnType;
private ITypeReference reference; // lazily constructed
/**
* Creates a type info suitable for the binding.
* @param index a non-null index in which to locate references
* @param binding
* @since 4.0.1
*/
public static IndexTypeInfo create(IIndex index, IIndexBinding binding) {
String[] fqn;
int elementType;
IIndexFileLocation flsq = null;
try {
elementType = IndexModelUtil.getElementType(binding);
if (binding instanceof ICPPBinding) {
fqn = ((ICPPBinding) binding).getQualifiedName();
} else if (binding instanceof IField) {
IField field = (IField) binding;
ICompositeType owner = field.getCompositeTypeOwner();
fqn = new String[] { owner.getName(), field.getName() };
} else {
fqn = new String[] { binding.getName() };
}
try {
IIndexFile file = binding.getLocalToFile();
if (file != null) {
flsq = file.getLocation();
}
} catch (CoreException e) {
}
if (binding instanceof IFunction) {
final IFunction function = (IFunction) binding;
final String[] paramTypes = IndexModelUtil.extractParameterTypes(function);
final String returnType = IndexModelUtil.extractReturnType(function);
return new IndexTypeInfo(fqn, flsq, elementType, index, paramTypes, returnType, null);
}
} catch (DOMException e) {
// Index bindings don't throw DOMExceptions.
throw new AssertionError();
}
return new IndexTypeInfo(fqn, flsq, elementType, index, null, null, null);
}
/**
* Creates a type info object suitable for a macro.
* @param index a non-null index in which to locate references
* @param macro a macro to create a type info for
* @since 4.0.1
*/
public static IndexTypeInfo create(IIndex index, IIndexMacro macro) {
final char[] name = macro.getNameCharArray();
final char[][] ps = macro.getParameterList();
String[] params = null;
if (ps != null) {
params = new String[ps.length];
int i = -1;
for (char[] p : ps) {
params[++i] = new String(p);
}
}
return new IndexTypeInfo(new String[] { new String(name) }, ICElement.C_MACRO, params, null, index);
}
/**
* @since 5.1
*/
public static IndexTypeInfo create(IndexTypeInfo rhs, ITypeReference ref) {
return new IndexTypeInfo(rhs, ref);
}
private IndexTypeInfo(String[] fqn, IIndexFileLocation fileLocal, int elementType, IIndex index, String[] params,
String returnType, ITypeReference reference) {
Assert.isTrue(index != null);
this.fqn = fqn;
this.fileLocal = fileLocal;
this.elementType = elementType;
this.index = index;
this.params = params;
this.returnType = returnType;
this.reference = reference;
}
private IndexTypeInfo(String[] fqn, int elementType, String[] params, String returnType, IIndex index) {
this(fqn, null, elementType, index, params, returnType, null);
}
private IndexTypeInfo(IndexTypeInfo rhs, ITypeReference ref) {
this(rhs.fqn, rhs.fileLocal, rhs.elementType, rhs.index, rhs.params, rhs.returnType, ref);
}
@Override
public int getCElementType() {
return elementType;
}
@Override
public ICProject getEnclosingProject() {
if (getResolvedReference() != null) {
IProject project = reference.getProject();
if (project != null) {
return CCorePlugin.getDefault().getCoreModel().getCModel().getCProject(project.getName());
}
}
return null;
}
@Override
public String getName() {
return fqn[fqn.length - 1];
}
@Override
public IQualifiedTypeName getQualifiedTypeName() {
return new QualifiedTypeName(fqn);
}
@Override
public String[] getParameters() {
return params;
}
@Override
public String getReturnType() {
return returnType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + elementType;
result = prime * result + ((fileLocal == null) ? 0 : fileLocal.hashCode());
result = prime * result + IndexTypeInfo.hashCode(fqn);
result = prime * result + IndexTypeInfo.hashCode(params);
return result;
}
/**
* Type info objects are equal if they compute the same references.
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IndexTypeInfo other = (IndexTypeInfo) obj;
if (elementType != other.elementType)
return false;
if (fileLocal == null) {
if (other.fileLocal != null)
return false;
} else if (!fileLocal.equals(other.fileLocal)) {
return false;
}
if (!Arrays.equals(fqn, other.fqn))
return false;
if (!Arrays.equals(params, other.params))
return false;
return true;
}
/**
* @since 5.1
*/
public boolean isFileLocal() {
return fileLocal != null;
}
@Override
public ITypeReference getResolvedReference() {
if (reference == null) {
if (elementType == ICElement.C_MACRO) {
return createMacroReference();
}
try {
index.acquireReadLock();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return null;
}
try {
IIndexBinding[] ibs = findBindings();
if (ibs.length > 0) {
IIndexName[] names;
names = index.findNames(ibs[0], IIndex.FIND_DEFINITIONS);
if (names.length == 0) {
names = index.findNames(ibs[0], IIndex.FIND_DECLARATIONS);
}
for (IIndexName name : names) {
reference = createReference(ibs[0], name);
if (reference != null) {
break;
}
}
}
} catch (CoreException e) {
CCorePlugin.log(e);
} finally {
index.releaseReadLock();
}
}
return reference;
}
private IIndexBinding[] findBindings() throws CoreException {
char[][] cfqn = new char[fqn.length][];
for (int i = 0; i < fqn.length; i++) {
cfqn[i] = fqn[i].toCharArray();
}
IIndexBinding[] ibs = index.findBindings(cfqn, new IndexFilter() {
@Override
public boolean acceptBinding(IBinding binding) {
if (!IndexModelUtil.bindingHasCElementType(binding, new int[] { elementType })) {
return false;
}
try {
if (fileLocal == null) {
if (((IIndexBinding) binding).isFileLocal()) {
return false;
}
} else {
IIndexFile localToFile = ((IIndexBinding) binding).getLocalToFile();
if (localToFile == null || !fileLocal.equals(localToFile.getLocation())) {
return false;
}
}
if (binding instanceof IFunction && params != null) {
String[] otherParams = IndexModelUtil.extractParameterTypes((IFunction) binding);
if (!Arrays.equals(params, otherParams)) {
return false;
}
}
} catch (CoreException | DOMException e) {
CCorePlugin.log(e);
}
return true;
}
}, new NullProgressMonitor());
return ibs;
}
private ITypeReference createMacroReference() {
try {
index.acquireReadLock();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return null;
}
try {
IIndexMacro[] macros = index.findMacros(fqn[0].toCharArray(), IndexFilter.ALL_DECLARED,
new NullProgressMonitor());
if (macros.length > 0) {
for (IIndexMacro macro : macros) {
reference = createReference(macro);
if (reference != null) {
break;
}
}
}
} catch (CoreException e) {
CCorePlugin.log(e);
} finally {
index.releaseReadLock();
}
return reference;
}
private IndexTypeReference createReference(IIndexBinding binding, IIndexName indexName) throws CoreException {
IIndexFileLocation ifl = indexName.getFile().getLocation();
String fullPath = ifl.getFullPath();
if (fullPath != null) {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fullPath));
if (file != null) {
return new IndexTypeReference(binding, file, file.getProject(), indexName.getNodeOffset(),
indexName.getNodeLength());
}
} else {
IPath path = URIUtil.toPath(ifl.getURI());
if (path != null) {
return new IndexTypeReference(binding, path, null, indexName.getNodeOffset(),
indexName.getNodeLength());
}
}
return null;
}
private IndexTypeReference createReference(IIndexMacro macro) throws CoreException {
IIndexName def = macro.getDefinition();
if (def != null) {
return createReference(macro, def);
}
return null;
}
@Override
public ITypeReference[] getReferences() {
if (elementType == ICElement.C_MACRO) {
return getMacroReferences();
}
List<IndexTypeReference> references = new ArrayList<>();
try {
index.acquireReadLock();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return new ITypeReference[0];
}
try {
IIndexBinding[] ibs = findBindings();
HashMap<IIndexFileLocation, IIndexFile> iflMap = new HashMap<>();
for (IIndexBinding binding : ibs) {
IIndexName[] names;
names = index.findNames(binding, IIndex.FIND_DEFINITIONS);
if (names.length == 0) {
names = index.findNames(binding, IIndex.FIND_DECLARATIONS);
}
for (IIndexName indexName : names) {
if (checkFile(iflMap, indexName.getFile())) {
IndexTypeReference ref = createReference(binding, indexName);
if (ref != null) {
references.add(ref);
}
}
}
}
} catch (CoreException ce) {
CCorePlugin.log(ce);
} finally {
index.releaseReadLock();
}
return references.toArray(new IndexTypeReference[references.size()]);
}
private ITypeReference[] getMacroReferences() {
List<IndexTypeReference> references = new ArrayList<>();
try {
index.acquireReadLock();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return new ITypeReference[0];
}
try {
char[] cfn = fqn[0].toCharArray();
IIndexMacro[] ibs = index.findMacros(cfn, IndexFilter.ALL_DECLARED, new NullProgressMonitor());
// in case a file is represented multiple times in the index then we take references from
// one of those, only.
HashMap<IIndexFileLocation, IIndexFile> iflMap = new HashMap<>();
for (IIndexMacro macro : ibs) {
if (checkParameters(macro.getParameterList())) {
if (checkFile(iflMap, macro.getFile())) {
IndexTypeReference ref = createReference(macro);
if (ref != null) {
references.add(ref);
}
}
}
}
} catch (CoreException e) {
CCorePlugin.log(e);
} finally {
index.releaseReadLock();
}
return references.toArray(new IndexTypeReference[references.size()]);
}
private boolean checkParameters(char[][] parameterList) {
if (parameterList == null) {
return params == null;
}
if (params == null || parameterList.length != params.length) {
return false;
}
for (int i = 0; i < parameterList.length; i++) {
if (!params[i].equals(new String(parameterList[i]))) {
return false;
}
}
return true;
}
/**
* Makes sure that per file only refs from one IIndexFile object are taken.
*/
private boolean checkFile(HashMap<IIndexFileLocation, IIndexFile> iflMap, IIndexFile file) throws CoreException {
IIndexFileLocation ifl = file.getLocation();
IIndexFile otherFile = iflMap.get(ifl);
if (otherFile == null) {
iflMap.put(ifl, file);
return true;
}
return otherFile.equals(file);
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public void addDerivedReference(ITypeReference location) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public void addReference(ITypeReference location) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean canSubstituteFor(ITypeInfo info) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean encloses(ITypeInfo info) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean exists() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeReference[] getDerivedReferences() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo[] getEnclosedTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo[] getEnclosedTypes(int[] kinds) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo getEnclosingNamespace(boolean includeGlobalNamespace) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo getEnclosingType() {
// TODO not sure
return null;
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo getEnclosingType(int[] kinds) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo getRootNamespace(boolean includeGlobalNamespace) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo[] getSubTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ASTAccessVisibility getSuperTypeAccess(ITypeInfo subType) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public ITypeInfo[] getSuperTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean hasEnclosedTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean hasSubTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean hasSuperTypes() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isClass() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isEnclosed(ITypeInfo info) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isEnclosed(ITypeSearchScope scope) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isEnclosedType() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isEnclosingType() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isReferenced(ITypeSearchScope scope) {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public boolean isUndefinedType() {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* @noreference This method is not intended to be referenced by clients.
*/
@Override
@Deprecated
public void setCElementType(int type) {
throw new UnsupportedOperationException();
}
}