Skip to content

Commit

Permalink
feature: NameScope detects conflicts on names
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Dec 1, 2018
1 parent 7ade841 commit ae93fe0
Show file tree
Hide file tree
Showing 5 changed files with 567 additions and 0 deletions.
195 changes: 195 additions & 0 deletions src/main/java/spoon/reflect/visitor/NameScope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* Copyright (C) 2006-2018 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.visitor;

import java.lang.annotation.Annotation;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.function.Function;

import spoon.SpoonException;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtCatch;
import spoon.reflect.code.CtLambda;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtAnnotationType;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtCompilationUnit;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtEnum;
import spoon.reflect.declaration.CtInterface;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtNamedElement;
import spoon.support.Experimental;

/**
* Maps names to CtElements, which are visible at current scanning place
*/
@Experimental
public abstract class NameScope {

private static NameScope EMPTY = new NameScope(null, null) {
@Override
protected <T> T forEachLocalElementByName(String name, Function<? super CtNamedElement, T> consumer) {
return null;
}
};

/**
* The {@link EarlyTerminatingScanner} implementation, which knows all visible elements in the scope
* @param <T>
*/
public static class Scanner<T> extends EarlyTerminatingScanner<T> {
private final Deque<NameScope> scopes = new ArrayDeque<>();
protected void enter(spoon.reflect.declaration.CtElement e) {
NameScope newFinder = NameScope.onElement(scopes.peek(), e);
if (newFinder != null) {
scopes.push(newFinder);
}
}
protected void exit(spoon.reflect.declaration.CtElement e) {
NameScope topFinder = scopes.peek();
if (topFinder != null && topFinder.getScopeElement() == e) {
//we are living scope of this ConflictFinder. Pop it
scopes.pop();
}
}
public NameScope getNameScope() {
NameScope ns = scopes.peek();
return ns == null ? EMPTY : ns;
}
public Deque<NameScope> getScopes() {
return scopes;
}
}

/**
* Call it for each visited CtElement
* @param parent the parent ConflictFinder
* @param target an element
* @return new {@link NameScope} if `target` element declares new naming scope or null if there is no new scope
*/
protected static NameScope onElement(NameScope parent, CtElement target) {
class Visitor extends CtAbstractVisitor {
NameScope finder = null;
@Override
public void visitCtCompilationUnit(CtCompilationUnit compilationUnit) {
finder = new SimpleNameScope(parent, compilationUnit);
}
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
finder = new NameScopeOfType(parent, ctClass);
}
@Override
public <T> void visitCtInterface(CtInterface<T> intrface) {
finder = new NameScopeOfType(parent, intrface);
}
@Override
public <T extends Enum<?>> void visitCtEnum(CtEnum<T> ctEnum) {
finder = new NameScopeOfType(parent, ctEnum);
}
@Override
public <A extends Annotation> void visitCtAnnotationType(CtAnnotationType<A> annotationType) {
finder = new NameScopeOfType(parent, annotationType);
}
@Override
public <T> void visitCtMethod(CtMethod<T> m) {
finder = new SimpleNameScope(parent, m, m.getParameters());
}
@Override
public <T> void visitCtConstructor(CtConstructor<T> c) {
finder = new SimpleNameScope(parent, c, c.getParameters());
}
@Override
public <T> void visitCtLambda(CtLambda<T> lambda) {
finder = new SimpleNameScope(parent, lambda, lambda.getParameters());
}
@Override
public void visitCtCatch(CtCatch catchBlock) {
finder = new SimpleNameScope(parent, catchBlock).addVariable(catchBlock.getParameter());
}
@Override
public <R> void visitCtBlock(CtBlock<R> block) {
finder = new SimpleNameScope(parent, block);
}
@Override
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
if (parent instanceof SimpleNameScope) {
((SimpleNameScope) parent).addVariable(localVariable);
} else {
throw new SpoonException("Cannot add local variable when parent is missing");
}
}
};
Visitor scanner = new Visitor();
target.accept(scanner);
return scanner.finder;
}

private final NameScope parent;
private final CtElement scopeElement;

protected NameScope(NameScope parent, CtElement scopeElement) {
this.parent = parent;
this.scopeElement = scopeElement;
}

/**
* @return the {@link CtElement} which represents the current scope
*/
public final CtElement getScopeElement() {
return scopeElement;
}

/**
* @param name to be searched simple name
* @param consumer is called for each named element with same name which are accessible from this {@link NameScope}
* as long as there are some elements and consumer returns null. If `consumer` return not null value then it is returned
* @return the value returned by `consumer` or null
*/
public final <T> T forEachElementByName(String name, Function<? super CtNamedElement, T> consumer) {
T r = forEachLocalElementByName(name, consumer);
if (r != null) {
return r;
}
if (scopeElement instanceof CtNamedElement) {
CtNamedElement named = (CtNamedElement) scopeElement;
if (name.equals(named.getSimpleName())) {
r = consumer.apply(named);
if (r != null) {
return r;
}
}
}
if (parent != null) {
return parent.forEachElementByName(name, consumer);
}
return null;
}

protected abstract <T> T forEachLocalElementByName(String name, Function<? super CtNamedElement, T> consumer);

protected static <T> T forEachByName(Map<String, CtNamedElement> map, String name, Function<? super CtNamedElement, T> consumer) {
CtNamedElement named = map.get(name);
if (named != null) {
return consumer.apply(named);
}
return null;
}
}
86 changes: 86 additions & 0 deletions src/main/java/spoon/reflect/visitor/NameScopeOfType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Copyright (C) 2006-2018 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.visitor;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.visitor.filter.AllTypeMembersFunction;

/**
* Represents NameScope of Type. Knows all accessible fields, nested type names and method names
*/
class NameScopeOfType extends NameScope {
private Map<String, CtNamedElement> fieldsByNam;
private Map<String, CtNamedElement> typesByName;
private Map<String, CtNamedElement> methodsByName;

NameScopeOfType(NameScope conflictFinder, CtType<?> p_type) {
super(conflictFinder, p_type);
}

@Override
protected <T> T forEachLocalElementByName(String name, Function<? super CtNamedElement, T> consumer) {
assureCacheInitialized();
T r = forEachByName(fieldsByNam, name, consumer);
if (r != null) {
return r;
}
r = forEachByName(typesByName, name, consumer);
if (r != null) {
return r;
}
r = forEachByName(methodsByName, name, consumer);
if (r != null) {
return r;
}
return null;
}

private void assureCacheInitialized() {
if (fieldsByNam == null) {
//collect names of type members which are visible in this type
fieldsByNam = new HashMap<>();
typesByName = new HashMap<>();
methodsByName = new HashMap<>();
getScopeElement().map(new AllTypeMembersFunction().setMode(AllTypeMembersFunction.Mode.SKIP_PRIVATE)).forEach((CtTypeMember typeMember) -> {
//the local members are visited first. Then members of super types/interfaces
if (typeMember instanceof CtField) {
putIfNotExists(fieldsByNam, (CtField<?>) typeMember);
} else if (typeMember instanceof CtType) {
putIfNotExists(typesByName, (CtType<?>) typeMember);
} else if (typeMember instanceof CtMethod) {
putIfNotExists(methodsByName, (CtMethod<?>) typeMember);
}
});
}
}

//assures that type members nearer to local type are used
private <T extends CtNamedElement> void putIfNotExists(Map<String, T> map, T element) {
String name = element.getSimpleName();
if (!map.containsKey(name)) {
map.put(name, element);
}
}
}
Loading

0 comments on commit ae93fe0

Please sign in to comment.