/*******************************************************************************
* Copyright (c) 2008, 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
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.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDirective;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates;
import org.eclipse.cdt.internal.core.index.IIndexScope;
/**
* Utility to map index-scopes to scopes from the AST. This is important for
* scopes that can be reopened, i.e. namespaces.
*/
public class CPPScopeMapper {
/**
* Used for implicit inline directives for inline namespaces found in the index.
*/
public static final class InlineNamespaceDirective implements ICPPUsingDirective {
private final ICPPInternalNamespaceScope fContainer;
private final ICPPInternalNamespaceScope fNominated;
public InlineNamespaceDirective(ICPPInternalNamespaceScope container, ICPPInternalNamespaceScope inline) {
fContainer= container;
fNominated= inline;
}
@Override
public IScope getContainingScope() {
return fContainer;
}
@Override
public ICPPNamespaceScope getNominatedScope() throws DOMException {
return fNominated;
}
@Override
public int getPointOfDeclaration() {
return 0;
}
}
/**
* Wrapper for namespace-scopes from the index.
*/
private class NamespaceScopeWrapper implements ICPPInternalNamespaceScope {
private final ICPPNamespaceScope fScope;
private ArrayList<ICPPUsingDirective> fUsingDirectives;
private ICPPNamespaceScope[] fEnclosingNamespaceSet;
public NamespaceScopeWrapper(ICPPNamespaceScope scope) {
fScope= scope;
assert fScope instanceof IIndexScope;
}
@Override
public EScopeKind getKind() {
return fScope.getKind();
}
@Override
public IBinding[] find(String name) {
return fScope.find(name);
}
@Override
public IBinding getBinding(IASTName name, boolean resolve) {
return fScope.getBinding(name, resolve);
}
@Override
public IBinding getBinding(IASTName name, boolean resolve, IIndexFileSet acceptLocalBindings) {
return fScope.getBinding(name, resolve, acceptLocalBindings);
}
@Override @Deprecated
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup) {
return fScope.getBindings(name, resolve, prefixLookup);
}
@Override @Deprecated
public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet acceptLocalBindings) {
return getBindings(name, resolve, prefixLookup, acceptLocalBindings);
}
@Override
public IBinding[] getBindings(ScopeLookupData lookup) {
return fScope.getBindings(lookup);
}
@Override
public IScope getParent() throws DOMException {
IScope parent= fScope.getParent();
if (parent instanceof IIndexScope) {
return mapToASTScope((IIndexScope) parent);
}
return fTu.getScope();
}
@Override
public IName getScopeName() {
return fScope.getScopeName();
}
@Override
public void addUsingDirective(ICPPUsingDirective usingDirective) {
initUsingDirectives();
fUsingDirectives.add(usingDirective);
}
private void initUsingDirectives() {
if (fUsingDirectives == null) {
fUsingDirectives= new ArrayList<ICPPUsingDirective>(1);
// Insert a using directive for every inline namespace
for (ICPPInternalNamespaceScope inline: getInlineNamespaces()) {
fUsingDirectives.add(new InlineNamespaceDirective(this, inline));
}
}
}
@Override
public ICPPUsingDirective[] getUsingDirectives() {
initUsingDirectives();
return fUsingDirectives.toArray(new ICPPUsingDirective[fUsingDirectives.size()]);
}
@Override
public ICPPNamespaceScope[] getEnclosingNamespaceSet() {
if (fEnclosingNamespaceSet == null)
return fEnclosingNamespaceSet= CPPNamespaceScope.computeEnclosingNamespaceSet(this);
return fEnclosingNamespaceSet;
}
@Override
public boolean isInlineNamepace() {
IIndexBinding binding = ((IIndexScope) fScope).getScopeBinding();
if (binding instanceof ICPPNamespace && ((ICPPNamespace) binding).isInline())
return true;
return false;
}
@Override
public ICPPInternalNamespaceScope[] getInlineNamespaces() {
// Obtain the inline namespaces from the index and map them to the ast
ICPPNamespaceScope[] pre = fScope.getInlineNamespaces();
ICPPInternalNamespaceScope[] result= new ICPPInternalNamespaceScope[pre.length];
for (int i = 0; i < result.length; i++) {
result[i]= (ICPPInternalNamespaceScope) mapToASTScope((IIndexScope) pre[i]);
}
return result;
}
}
/**
* Wrapper for using directives from the index.
*/
private class UsingDirectiveWrapper implements ICPPUsingDirective {
private final int fOffset;
private final ICPPUsingDirective fDirective;
public UsingDirectiveWrapper(int offset, ICPPUsingDirective ud) {
fOffset= offset;
fDirective= ud;
}
@Override
public IScope getContainingScope() {
final IScope scope= fDirective.getContainingScope();
if (scope == null) {
return fTu.getScope();
}
return scope;
}
@Override
public ICPPNamespaceScope getNominatedScope() throws DOMException {
return fDirective.getNominatedScope();
}
@Override
public int getPointOfDeclaration() {
return fOffset;
}
}
/**
* Collector for class definitions.
*/
private class Visitor extends ASTVisitor {
Visitor() {
shouldVisitDeclarations = true;
}
@Override
public int visit(IASTDeclaration declaration) {
if (declaration instanceof IASTSimpleDeclaration) {
IASTDeclSpecifier declspec = ((IASTSimpleDeclaration) declaration).getDeclSpecifier();
if (declspec instanceof IASTCompositeTypeSpecifier) {
IASTCompositeTypeSpecifier cts = (IASTCompositeTypeSpecifier) declspec;
final IASTName name = cts.getName();
final char[] nameChars = name.getLookupKey();
if (nameChars.length > 0) {
IASTName[] names= fClasses.get(nameChars);
names= ArrayUtil.append(IASTName.class, names, name);
fClasses.put(nameChars, names);
}
return PROCESS_CONTINUE;
}
return PROCESS_SKIP;
} else if (declaration instanceof IASTASMDeclaration
|| declaration instanceof IASTFunctionDefinition) {
return PROCESS_SKIP;
}
return PROCESS_CONTINUE;
}
}
private final HashMap<IIndexScope, IScope> fMappedScopes= new HashMap<IIndexScope, IScope>();
private final HashMap<String, NamespaceScopeWrapper> fNamespaceWrappers= new HashMap<String, NamespaceScopeWrapper>();
private final Map<String, List<UsingDirectiveWrapper>> fPerName= new HashMap<String, List<UsingDirectiveWrapper>>();
private final CPPASTTranslationUnit fTu;
protected CharArrayMap<IASTName[]> fClasses;
public CPPScopeMapper(CPPASTTranslationUnit tu) {
fTu= tu;
}
/**
* Register an additional list of using directives to be considered.
* @param offset the global offset at which the using directives are provided
* @param usingDirectives the list of additional directives.
*/
public void registerAdditionalDirectives(int offset, List<ICPPUsingDirective> usingDirectives) {
if (!usingDirectives.isEmpty()) {
for (ICPPUsingDirective ud : usingDirectives) {
IScope container= ud.getContainingScope();
try {
final String name= getReverseQualifiedName(container);
List<UsingDirectiveWrapper> list= fPerName.get(name);
if (list == null) {
list= new LinkedList<UsingDirectiveWrapper>();
fPerName.put(name, list);
}
list.add(new UsingDirectiveWrapper(offset, ud));
} catch (DOMException e) {
}
}
}
}
/**
* Adds additional directives previously registered to the given scope.
*/
public void handleAdditionalDirectives(ICPPNamespaceScope scope) {
assert !(scope instanceof IIndexScope);
if (fPerName.isEmpty()) {
return;
}
try {
String qname = getReverseQualifiedName(scope);
List<UsingDirectiveWrapper> candidates= fPerName.remove(qname);
if (candidates != null) {
for (UsingDirectiveWrapper ud : candidates) {
scope.addUsingDirective(ud);
}
}
} catch (DOMException e) {
}
}
private String getReverseQualifiedName(IScope scope) throws DOMException {
final CPPNamespaceScope tuscope = fTu.getScope();
if (scope == tuscope || scope == null) {
return ""; //$NON-NLS-1$
}
StringBuilder buf= new StringBuilder();
IName scopeName = scope.getScopeName();
if (scopeName != null) {
buf.append(scopeName.getSimpleID());
}
scope= scope.getParent();
while (scope != null && scope != tuscope) {
buf.append(':');
scopeName= scope.getScopeName();
if (scopeName != null) {
buf.append(scope.getScopeName().getSimpleID());
}
scope= scope.getParent();
}
return buf.toString();
}
/**
* Maps namespace scopes from the index back into the AST.
*/
public IScope mapToASTScope(IIndexScope scope) {
if (scope == null) {
return fTu.getScope();
}
if (scope instanceof ICPPNamespaceScope) {
IScope result= fMappedScopes.get(scope);
if (result == null) {
result= fTu.getScope().findNamespaceScope(scope);
if (result == null) {
result= wrapNamespaceScope((ICPPNamespaceScope) scope);
}
fMappedScopes.put(scope, result);
}
return result;
}
return scope;
}
private IScope wrapNamespaceScope(ICPPNamespaceScope scope) {
try {
String rqname= getReverseQualifiedName(scope);
NamespaceScopeWrapper result= fNamespaceWrappers.get(rqname);
if (result == null) {
result= new NamespaceScopeWrapper(getCompositeNamespaceScope(scope));
fNamespaceWrappers.put(rqname, result);
}
return result;
} catch (DOMException e) {
assert false; // index scopes don't throw dom-exceptions
return null;
}
}
private ICPPNamespaceScope getCompositeNamespaceScope(ICPPNamespaceScope scope) throws DOMException {
if (scope instanceof IIndexScope) {
IIndexBinding binding= fTu.getIndex().adaptBinding(((IIndexScope) scope).getScopeBinding());
if (binding instanceof ICPPNamespace) {
scope= ((ICPPNamespace) binding).getNamespaceScope();
}
}
return scope;
}
public ICPPClassType mapToAST(ICPPClassType type, IASTNode point) {
if (type instanceof ICPPTemplateInstance) {
ICPPTemplateInstance inst= (ICPPTemplateInstance) type;
ICPPTemplateDefinition template= inst.getTemplateDefinition();
if (template instanceof IIndexBinding && template instanceof ICPPClassType) {
IBinding mapped= mapToAST((ICPPClassType) template, point);
if (mapped != template && mapped instanceof ICPPClassType) {
mapped= CPPTemplates.instantiate((ICPPClassTemplate) mapped, inst.getTemplateArguments(), point);
if (mapped instanceof ICPPClassType)
return (ICPPClassType) mapped;
}
}
return type;
}
if (fClasses == null) {
fClasses= new CharArrayMap<IASTName[]>();
fTu.accept(new Visitor());
}
IASTName[] names= fClasses.get(type.getNameCharArray());
if (names != null) {
for (IASTName name : names) {
if (name == null)
break;
IBinding b= name.resolveBinding();
if (b instanceof ICPPClassType) {
final ICPPClassType mapped = (ICPPClassType) b;
if (mapped.isSameType(type)) {
return mapped;
}
}
}
}
return type;
}
}