/*******************************************************************************
* Copyright (c) 2005, 2015 QNX Software Systems 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:
* Doug Schaefer (QNX) - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Andrew Ferguson (Symbian)
* Bryan Wilkinson (QNX)
* Sergey Prigogin (Google)
* Jens Elmenthaler - http://bugs.eclipse.org/173458 (camel case completion)
*******************************************************************************/
package org.eclipse.cdt.internal.core.pdom.dom.cpp;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.IPDOMNode;
import org.eclipse.cdt.core.dom.IPDOMVisitor;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IASTName;
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.cpp.ICPPASTConversionName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.index.DeclaredBindingsFilter;
import org.eclipse.cdt.internal.core.index.IIndexScope;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.dom.BindingCollector;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.core.runtime.CoreException;
/**
* Represents the class scope for a class stored in the index.
* For safe use, all fields need to be final.
*/
class PDOMCPPClassScope implements ICPPClassScope, IIndexScope {
private static final class PopulateMap implements IPDOMVisitor {
private final CharArrayObjectMap<List<PDOMBinding>> fResult;
private PopulateMap(CharArrayObjectMap<List<PDOMBinding>> result) {
fResult = result;
}
@Override
public boolean visit(IPDOMNode node) throws CoreException {
if (node instanceof PDOMBinding) {
final PDOMBinding binding= (PDOMBinding) node;
final char[] nchars = binding.getNameCharArray();
List<PDOMBinding> list= fResult.get(nchars);
if (list == null) {
list= new ArrayList<>();
fResult.put(nchars, list);
}
list.add(binding);
if (binding instanceof ICompositeType && ((ICompositeType) binding).isAnonymous()) {
return true; // visit children
}
}
return false;
}
@Override
public void leave(IPDOMNode node){}
}
private static final IndexFilter CONVERSION_FILTER =
new DeclaredBindingsFilter(ILinkage.CPP_LINKAGE_ID, true, false) {
@Override
public boolean acceptBinding(IBinding binding) throws CoreException {
return binding instanceof ICPPMethod &&
SemanticUtil.isConversionOperator(((ICPPMethod) binding)) &&
super.acceptBinding(binding);
}
};
private final IPDOMCPPClassType fBinding;
public PDOMCPPClassScope(IPDOMCPPClassType binding) {
fBinding= binding;
}
@Override
public EScopeKind getKind() {
return EScopeKind.eClassType;
}
@Override
public ICPPClassType getClassType() {
return fBinding;
}
@Override
public IBinding getBinding(IASTName name, boolean resolve) {
return getBinding(name, resolve, null);
}
@Override
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup) {
return getBindings(new ScopeLookupData(name, resolve, prefixLookup));
}
@Override
public IBinding getBinding(IASTName name, boolean resolve, IIndexFileSet fileSet) {
try {
final char[] nameChars = name.getSimpleID();
if (CharArrayUtils.equals(fBinding.getNameCharArray(), nameChars)) {
// 9.2 ... The class-name is also inserted into the scope of the class itself
return getClassNameBinding();
}
final IBinding[] candidates = getBindingsViaCache(fBinding, nameChars, IndexFilter.CPP_DECLARED_OR_IMPLICIT_NO_INSTANCE);
return CPPSemantics.resolveAmbiguities(name, candidates);
} catch (CoreException e) {
CCorePlugin.log(e);
}
return null;
}
private IBinding getClassNameBinding() {
return fBinding;
}
@Deprecated @Override
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet fileSet) {
return getBindings(new ScopeLookupData(name, resolve, prefixLookup));
}
@Override
public IBinding[] getBindings(ScopeLookupData lookup) {
try {
if (lookup.getLookupName() instanceof ICPPASTConversionName) {
BindingCollector visitor = new BindingCollector(fBinding.getLinkage(),
Keywords.cOPERATOR, CONVERSION_FILTER, true, false, true);
acceptViaCache(fBinding, visitor, true);
return visitor.getBindings();
}
final char[] nameChars = lookup.getLookupKey();
if (!lookup.isPrefixLookup()) {
if (CharArrayUtils.equals(fBinding.getNameCharArray(), nameChars)) {
if (CPPClassScope.shallReturnConstructors(lookup.getLookupName(), false)){
return ClassTypeHelper.getConstructors(fBinding, lookup.getLookupPoint());
}
return new IBinding[] { getClassNameBinding() };
}
return getBindingsViaCache(fBinding, nameChars, IndexFilter.CPP_DECLARED_OR_IMPLICIT_NO_INSTANCE);
}
// Prefix lookup.
BindingCollector visitor = new BindingCollector(fBinding.getLinkage(), nameChars,
IndexFilter.CPP_DECLARED_OR_IMPLICIT_NO_INSTANCE, true, true, false);
if (ContentAssistMatcherFactory.getInstance().match(nameChars, fBinding.getNameCharArray())) {
// Add the class itself, constructors will be found during the visit.
visitor.visit((IPDOMNode) getClassNameBinding());
}
acceptViaCache(fBinding, visitor, true);
return visitor.getBindings();
} catch (CoreException e) {
CCorePlugin.log(e);
}
return null;
}
public static IBinding[] getBindingsViaCache(IPDOMCPPClassType ct, final char[] name, IndexFilter filter) throws CoreException {
CharArrayObjectMap<List<PDOMBinding>> map = getBindingMap(ct);
List<PDOMBinding> cached= map.get(name);
if (cached == null)
return IBinding.EMPTY_BINDING_ARRAY;
int i= 0;
IBinding[] result= new IBinding[cached.size()];
for (IBinding binding : cached) {
if (filter.acceptBinding(binding)) {
result[i++]= binding;
}
}
if (i == result.length)
return result;
final IBinding[] bresult= new IBinding[i];
System.arraycopy(result, 0, bresult, 0, i);
return bresult;
}
/**
* Visit bindings via the cache.
*/
public static void acceptViaCache(IPDOMCPPClassType ct, IPDOMVisitor visitor,
boolean includeNestedInAnonymous) throws CoreException {
final long record= ct.getRecord();
CharArrayObjectMap<List<PDOMBinding>> map= getBindingMap(ct);
for (List<PDOMBinding> list : map.values()) {
for (PDOMBinding node : list) {
if (includeNestedInAnonymous || node.getParentNodeRec() == record) {
if (visitor.visit(node)) {
node.accept(visitor);
}
visitor.leave(node);
}
}
}
}
public static void updateCache(IPDOMCPPClassType ct, PDOMNode member) throws CoreException {
if (member instanceof PDOMBinding) {
final Long key= ct.getRecord() + PDOMCPPLinkage.CACHE_MEMBERS;
final PDOM pdom = ct.getPDOM();
@SuppressWarnings("unchecked")
Reference<CharArrayObjectMap<List<PDOMBinding>>> cached=
(Reference<CharArrayObjectMap<List<PDOMBinding>>>) pdom.getCachedResult(key);
CharArrayObjectMap<List<PDOMBinding>> map= cached == null ? null : cached.get();
if (map != null) {
new PopulateMap(map).visit(member);
}
}
}
public static CharArrayObjectMap<List<PDOMBinding>> getBindingMap(IPDOMCPPClassType ct) throws CoreException {
final Long key= ct.getRecord() + PDOMCPPLinkage.CACHE_MEMBERS;
final PDOM pdom = ct.getPDOM();
@SuppressWarnings("unchecked")
Reference<CharArrayObjectMap<List<PDOMBinding>>> cached=
(Reference<CharArrayObjectMap<List<PDOMBinding>>>) pdom.getCachedResult(key);
CharArrayObjectMap<List<PDOMBinding>> map= cached == null ? null : cached.get();
if (map == null) {
// There is no cache, build it:
map= new CharArrayObjectMap<>(8);
IPDOMVisitor visitor= new PopulateMap(map);
visitor.visit(ct);
ct.acceptUncached(visitor);
pdom.putCachedResult(key, new SoftReference<>(map));
}
return map;
}
@Override
public IBinding[] find(String name, IASTTranslationUnit tu) {
return CPPSemantics.findBindingsInScope(this, name, tu);
}
@Override @Deprecated
public IBinding[] find(String name) {
return CPPSemantics.findBindings(this, name, false);
}
@Override
public IIndexBinding getScopeBinding() {
return fBinding;
}
@Override
public ICPPMethod[] getImplicitMethods() {
try {
PDOMClassUtil.MethodCollector methods = new PDOMClassUtil.MethodCollector(true, false);
acceptViaCache(fBinding, methods, false);
return methods.getMethods();
} catch (CoreException e) {
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
}
}
@Override
public ICPPConstructor[] getConstructors() {
return fBinding.getConstructors();
}
@Override
public IIndexScope getParent() {
return fBinding.getScope();
}
@Override
public IIndexName getScopeName() {
return fBinding.getScopeName();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PDOMCPPClassScope)
return fBinding.equals(((PDOMCPPClassScope) obj).fBinding);
return false;
}
@Override
public int hashCode() {
return fBinding.hashCode();
}
}