/*******************************************************************************
* Copyright (c) 2009, 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
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
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.ICPPMember;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalUnknownScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
/**
* Helper class for performing the base class lookup. First a directed graph without loops is computed
* to represent the base class hierarchy up to those bases for which the lookup finds matches. Next, from
* these leaves we search for virtual bases that are hidden. With this information the matches are extracted
* from the graph.
*/
class BaseClassLookup {
public static void lookupInBaseClasses(LookupData data, ICPPClassScope classScope, IIndexFileSet fileSet) {
if (classScope == null)
return;
final ICPPClassType classType= classScope.getClassType();
if (classType == null)
return;
final HashMap<IScope, BaseClassLookup> infoMap = new HashMap<IScope, BaseClassLookup>();
BaseClassLookup rootInfo= lookupInBaseClass(data, null, false, classType, fileSet, infoMap, 0);
if (data.contentAssist) {
rootInfo.collectResultForContentAssist(data);
} else {
hideVirtualBases(rootInfo, infoMap);
IBinding[] result= rootInfo.collectResult(data, true, IBinding.EMPTY_BINDING_ARRAY);
if (data.problem == null) {
data.foundItems = ArrayUtil.addAll((Object[]) data.foundItems, result);
} else if (result.length > 0) {
data.problem.setCandidateBindings(result);
}
// verifyResult(data, result);
}
}
private final ICPPClassType fClassType;
private IBinding[] fBindings;
private List<BaseClassLookup> fChildren= Collections.emptyList();
private BitSet fVirtual;
private boolean fHiddenAsVirtualBase= false;
private boolean fPropagationDone= false;
private boolean fCollected;
private boolean fCollectedAsRegularBase;
private BaseClassLookup(ICPPClassType type) {
fClassType= type;
}
ICPPClassType getClassType() {
return fClassType;
}
IBinding[] getResult() {
return fBindings;
}
boolean containsVirtualBase() {
return (fVirtual != null && fVirtual.nextSetBit(0) >= 0);
}
boolean hasMatches() {
return fBindings != null && fBindings.length > 0 && fBindings[0] != null;
}
public void addBase(boolean virtual, BaseClassLookup baseInfo) {
if (virtual && fHiddenAsVirtualBase)
return;
if (fChildren.isEmpty()) {
fChildren= new ArrayList<BaseClassLookup>();
fVirtual= new BitSet();
}
fVirtual.set(fChildren.size(), virtual);
fChildren.add(baseInfo);
}
public void setResult(IBinding[] bindings) {
fBindings= bindings;
}
public void setHiddenAsVirtualBase() {
fHiddenAsVirtualBase= true;
}
public void propagateHiddenAsVirtual() {
if (fPropagationDone)
return;
fPropagationDone= true;
for (int i= 0; i < fChildren.size(); i++) {
BaseClassLookup child = fChildren.get(i);
if (fVirtual.get(i)) {
child.setHiddenAsVirtualBase();
}
child.propagateHiddenAsVirtual();
}
}
public boolean containsNonStaticMember() {
for (IBinding binding : fBindings) {
if (binding == null)
return false;
if (binding instanceof ICPPMember) {
if (!((ICPPMember) binding).isStatic())
return true;
}
}
return false;
}
static BaseClassLookup lookupInBaseClass(LookupData data, ICPPClassScope baseClassScope, boolean isVirtual,
ICPPClassType root, IIndexFileSet fileSet, HashMap<IScope, BaseClassLookup> infoMap, int depth) {
if (depth++ > CPPSemantics.MAX_INHERITANCE_DEPTH)
return null;
if (baseClassScope != null) {
BaseClassLookup info= infoMap.get(baseClassScope);
if (info != null) {
// avoid loops
if (info.getResult() == null) {
data.problem = new ProblemBinding(null, IProblemBinding.SEMANTIC_CIRCULAR_INHERITANCE,
root.getNameCharArray());
return null;
}
return info;
}
}
// this is the first time to handle the class
BaseClassLookup result;
IBinding[] matches= IBinding.EMPTY_BINDING_ARRAY;
if (baseClassScope == null) {
result= new BaseClassLookup(root);
infoMap.put(root.getCompositeScope(), result);
} else {
result= new BaseClassLookup(baseClassScope.getClassType());
infoMap.put(baseClassScope, result);
try {
IBinding[] members= CPPSemantics.getBindingsFromScope(baseClassScope, fileSet, data);
if (members != null && members.length > 0 && members[0] != null) {
if (data.prefixLookup) {
matches= members;
} else {
result.setResult(members);
return result;
}
}
} catch (DOMException e) {
// continue the lookup
}
}
// There is no result in the baseClass itself or we do content assist, we have to examine its
// base-classes
ICPPClassType baseClass= result.getClassType();
if (baseClass != null) {
ICPPBase[] grandBases= null;
grandBases= baseClass.getBases();
if (grandBases != null && grandBases.length > 0) {
HashSet<IBinding> grandBaseBindings= null;
BitSet selectedBases= null;
if (grandBases.length > 1) {
grandBaseBindings= new HashSet<IBinding>();
// if we have reachable bases, then ignore the others
selectedBases = selectPreferredBases(data, grandBases);
}
for (int i = 0; i < grandBases.length; i++) {
ICPPBase grandBase = grandBases[i];
if (selectedBases != null && !selectedBases.get(i))
continue;
IBinding grandBaseBinding = grandBase.getBaseClass();
if (!(grandBaseBinding instanceof ICPPClassType)) {
// 14.6.2.3 scope is not examined
if (grandBaseBinding instanceof ICPPUnknownBinding) {
if (data.skippedScope == null)
data.skippedScope= root;
}
continue;
}
ICPPClassType grandBaseClass = (ICPPClassType) grandBaseBinding;
if (data.contentAssist && grandBaseClass instanceof ICPPDeferredClassInstance) {
// Support content assist for members of deferred instances.
grandBaseClass= ((ICPPDeferredClassInstance) grandBaseClass).getClassTemplate();
}
if (grandBaseBindings != null && !grandBaseBindings.add(grandBaseClass))
continue;
final IScope grandBaseScope= grandBaseClass.getCompositeScope();
if (grandBaseScope == null || grandBaseScope instanceof ICPPInternalUnknownScope) {
// 14.6.2.3 scope is not examined
if (data.skippedScope == null)
data.skippedScope= root;
continue;
}
if (!(grandBaseScope instanceof ICPPClassScope))
continue;
BaseClassLookup baseInfo= lookupInBaseClass(data, (ICPPClassScope) grandBaseScope,
grandBase.isVirtual(), root, fileSet, infoMap, depth);
if (baseInfo != null)
result.addBase(grandBase.isVirtual(), baseInfo);
}
}
}
result.setResult(matches);
return result;
}
private static BitSet selectPreferredBases(LookupData data, ICPPBase[] grandBases) {
if (data.contentAssist)
return null;
BitSet selectedBases;
selectedBases= new BitSet(grandBases.length);
IName baseName= null;
for (int i = 0; i < grandBases.length; i++) {
ICPPBase nbase = grandBases[i];
if (nbase instanceof IProblemBinding)
continue;
final IName nbaseName = nbase.getBaseClassSpecifierName();
int cmp= baseName == null ? -1 : CPPSemantics.compareByRelevance(data, baseName, nbaseName);
if (cmp <= 0) {
if (cmp < 0) {
selectedBases.clear();
baseName= nbaseName;
}
selectedBases.set(i);
}
}
return selectedBases;
}
static void hideVirtualBases(BaseClassLookup rootInfo, HashMap<IScope, BaseClassLookup> infoMap) {
boolean containsVirtualBase= false;
final BaseClassLookup[] allInfos = infoMap.values().toArray(new BaseClassLookup[infoMap.size()]);
for (BaseClassLookup info : allInfos) {
if (info.containsVirtualBase()) {
containsVirtualBase= true;
break;
}
}
if (containsVirtualBase) {
for (BaseClassLookup info : allInfos) {
if (info.hasMatches()) {
info.hideVirtualBases(infoMap, 0);
}
}
}
}
void hideVirtualBases(HashMap<IScope, BaseClassLookup> infoMap, int depth) {
if (depth++ > CPPSemantics.MAX_INHERITANCE_DEPTH)
return;
if (fClassType != null) {
ICPPBase[] bases= null;
bases= fClassType.getBases();
if (bases != null && bases.length > 0) {
for (ICPPBase base : bases) {
IBinding baseBinding = base.getBaseClass();
if (!(baseBinding instanceof ICPPClassType)) {
continue;
}
final ICPPClassType baseClass = (ICPPClassType) baseBinding;
final IScope baseScope= baseClass.getCompositeScope();
if (!(baseScope instanceof ICPPClassScope))
continue;
BaseClassLookup baseInfo= infoMap.get(baseScope);
if (baseInfo != null) {
if (base.isVirtual()) {
baseInfo.setHiddenAsVirtualBase();
}
baseInfo.propagateHiddenAsVirtual();
} else {
// mark to catch recursions
baseInfo= new BaseClassLookup(baseClass);
infoMap.put(baseScope, baseInfo);
baseInfo.hideVirtualBases(infoMap, depth);
}
}
}
}
}
public void collectResultForContentAssist(LookupData data) {
if (fCollected)
return;
fCollected= true;
data.foundItems = CPPSemantics.mergePrefixResults((CharArrayObjectMap) data.foundItems, fBindings, true);
for (int i= 0; i < fChildren.size(); i++) {
BaseClassLookup child = fChildren.get(i);
child.collectResultForContentAssist(data);
}
}
private IBinding[] collectResult(LookupData data, boolean asVirtualBase, IBinding[] result) {
if (asVirtualBase) {
if (fHiddenAsVirtualBase)
return result;
} else {
if (fCollectedAsRegularBase && data.problem == null && containsNonStaticMember()) {
data.problem= new ProblemBinding(data.astName, IProblemBinding.SEMANTIC_AMBIGUOUS_LOOKUP);
}
fCollectedAsRegularBase= true;
}
if (fCollected)
return result;
fCollected= true;
int numBindingsToAdd = 0;
for (int i = 0; i < fBindings.length; i++) {
IBinding binding = fBindings[i];
if (binding == null)
break;
if (!ArrayUtil.contains(result, binding))
fBindings[numBindingsToAdd++] = binding;
}
if (numBindingsToAdd < fBindings.length)
fBindings[numBindingsToAdd] = null;
if (result.length > 0 && numBindingsToAdd > 0 && data.problem == null) {
// Matches are found in more than one base class - this is an indication of ambiguity.
data.problem= new ProblemBinding(data.astName,
IProblemBinding.SEMANTIC_AMBIGUOUS_LOOKUP, result);
}
result= ArrayUtil.addAll(result, fBindings);
for (int i= 0; i < fChildren.size(); i++) {
BaseClassLookup child = fChildren.get(i);
result= child.collectResult(data, fVirtual.get(i), result);
}
return result;
}
}