/*******************************************************************************
* Copyright (c) 2005, 2011 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.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.ICPPClassTemplatePartialSpecialization;
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.dom.ast.cpp.ICPPSpecialization;
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.CharArrayMap;
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.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 CharArrayMap<List<PDOMBinding>> fResult;
private PopulateMap(CharArrayMap<List<PDOMBinding>> result) {
fResult = result;
}
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<PDOMBinding>();
fResult.put(nchars, list);
}
list.add(binding);
if (binding instanceof ICompositeType && ((ICompositeType) binding).isAnonymous()) {
return true; // visit children
}
}
return false;
}
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;
}
public EScopeKind getKind() {
return EScopeKind.eClassType;
}
public ICPPClassType getClassType() {
return fBinding;
}
public IBinding getBinding(IASTName name, boolean resolve) {
return getBinding(name, resolve, null);
}
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup) {
return getBindings(name, resolve, prefixLookup, null);
}
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() {
if (fBinding instanceof ICPPClassTemplatePartialSpecialization)
return ((ICPPClassTemplatePartialSpecialization) fBinding).getPrimaryClassTemplate();
if (fBinding instanceof ICPPSpecialization)
return ((ICPPSpecialization) fBinding).getSpecializedBinding();
return fBinding;
}
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet fileSet) {
try {
if (name 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 = name.getSimpleID();
if (!prefixLookup) {
if (CharArrayUtils.equals(fBinding.getNameCharArray(), nameChars)) {
if (CPPClassScope.shallReturnConstructors(name, prefixLookup)){
return fBinding.getConstructors();
}
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, prefixLookup, prefixLookup, !prefixLookup);
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 {
CharArrayMap<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();
CharArrayMap<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<CharArrayMap<List<PDOMBinding>>> cached= (Reference<CharArrayMap<List<PDOMBinding>>>) pdom.getCachedResult(key);
CharArrayMap<List<PDOMBinding>> map= cached == null ? null : cached.get();
if (map != null) {
new PopulateMap(map).visit(member);
}
}
}
public static CharArrayMap<List<PDOMBinding>> getBindingMap(IPDOMCPPClassType ct) throws CoreException {
final Long key= ct.getRecord() + PDOMCPPLinkage.CACHE_MEMBERS;
final PDOM pdom = ct.getPDOM();
@SuppressWarnings("unchecked")
Reference<CharArrayMap<List<PDOMBinding>>> cached= (Reference<CharArrayMap<List<PDOMBinding>>>) pdom.getCachedResult(key);
CharArrayMap<List<PDOMBinding>> map= cached == null ? null : cached.get();
if (map == null) {
// there is no cache, build it:
map= new CharArrayMap<List<PDOMBinding>>();
IPDOMVisitor visitor= new PopulateMap(map);
visitor.visit(ct);
ct.acceptUncached(visitor);
pdom.putCachedResult(key, new SoftReference<CharArrayMap<?>>(map));
}
return map;
}
public IBinding[] find(String name) {
return CPPSemantics.findBindings( this, name, false );
}
public IIndexBinding getScopeBinding() {
return fBinding;
}
public ICPPMethod[] getImplicitMethods() {
try {
PDOMClassUtil.MethodCollector methods = new PDOMClassUtil.MethodCollector(true, false);
acceptViaCache(fBinding, methods, false);
return methods.getMethods();
} catch (CoreException e) {
return new ICPPMethod[0];
}
}
public ICPPConstructor[] getConstructors() {
return fBinding.getConstructors();
}
public IIndexScope getParent() {
return fBinding.getScope();
}
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();
}
}