/** * Copyright (C) 2006-2017 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.filter; import java.util.Collection; import spoon.reflect.code.CtBodyHolder; import spoon.reflect.code.CtCatch; import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtStatementList; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtParameter; import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.CtVariable; import spoon.reflect.reference.CtFieldReference; import spoon.reflect.visitor.chain.CtConsumableFunction; import spoon.reflect.visitor.chain.CtConsumer; import spoon.reflect.visitor.chain.CtQuery; import spoon.reflect.visitor.chain.CtQueryAware; /** * This mapping function searches for all {@link CtVariable} instances, * which might be a declaration of an input {@link CtElement}. * <br> * It can be used to search for variable declarations of * variable references and for detection of variable name conflicts * <br> * It returns {@link CtLocalVariable} instances, * or it returns {@link CtCatchVariable} instances of catch blocks, * or i returns {@link CtParameter} instances of methods, lambdas and catch blocks. * or it returns {@link CtField} instances from wrapping classes and their super classes too. * <br> * The elements are visited in the following order: first elements are thought in the nearest parent blocks, * then in the fields of wrapping classes, then in the fields of super classes, etc. * <br> * Example: Search for all potential {@link CtVariable} declarations<br> * <pre> {@code * CtVariableReference varRef = ...; * varRef.map(new PotentialVariableDeclarationFunction()).forEach(...process result...); * } * </pre> * Example: Search for {@link CtVariable} declaration of variable named `varName` in scope "scope" * <pre> {@code * CtElement scope = ...; * String varName = "anVariableName"; * CtVariable varOrNull = scope.map(new PotentialVariableDeclarationFunction(varName)).first(); * } * </pre> */ public class PotentialVariableDeclarationFunction implements CtConsumableFunction<CtElement>, CtQueryAware { private boolean isTypeOnTheWay; private final String variableName; private CtQuery query; public PotentialVariableDeclarationFunction() { this.variableName = null; } /** * Searches for a variable with exact name. * @param variableName */ public PotentialVariableDeclarationFunction(String variableName) { this.variableName = variableName; } @Override public void apply(CtElement input, CtConsumer<Object> outputConsumer) { isTypeOnTheWay = false; //Search previous siblings for element which may represents the declaration of this local variable CtQuery siblingsQuery = input.getFactory().createQuery() .map(new SiblingsFunction().mode(SiblingsFunction.Mode.PREVIOUS)) //select only CtVariable nodes .select(new TypeFilter<>(CtVariable.class)); if (variableName != null) { //variable name is defined so we have to search only for variables with that name siblingsQuery = siblingsQuery.select(new NameFilter<>(variableName)); } CtElement scopeElement = input; //Search input and then all parents until first CtPackage for element which may represents the declaration of this local variable while (scopeElement != null && !(scopeElement instanceof CtPackage) && scopeElement.isParentInitialized()) { CtElement parent = scopeElement.getParent(); if (parent instanceof CtType<?>) { isTypeOnTheWay = true; //TODO replace getAllFields() followed by getFieldDeclaration, by direct visiting of fields of types in super classes. Collection<CtFieldReference<?>> allFields = ((CtType<?>) parent).getAllFields(); for (CtFieldReference<?> fieldReference : allFields) { if (sendToOutput(fieldReference.getFieldDeclaration(), outputConsumer)) { return; } } } else if (parent instanceof CtBodyHolder || parent instanceof CtStatementList) { //visit all previous CtVariable siblings of scopeElement element in parent BodyHolder or Statement list siblingsQuery.setInput(scopeElement).forEach(outputConsumer); if (query.isTerminated()) { return; } //visit parameters of CtCatch and CtExecutable (method, lambda) if (parent instanceof CtCatch) { CtCatch ctCatch = (CtCatch) parent; if (sendToOutput(ctCatch.getParameter(), outputConsumer)) { return; } } else if (parent instanceof CtExecutable) { CtExecutable<?> exec = (CtExecutable<?>) parent; for (CtParameter<?> param : exec.getParameters()) { if (sendToOutput(param, outputConsumer)) { return; } } } } scopeElement = parent; } } /** * @param var * @param output * @return true if query processing is terminated */ private boolean sendToOutput(CtVariable<?> var, CtConsumer<Object> output) { if (variableName == null || variableName.equals(var.getSimpleName())) { output.accept(var); } return query.isTerminated(); } /** * This method provides access to current state of this function. * It is intended to be called by other mapping functions at query processing time or after query is finished. * * @return true if there is an local class on the way from the input of this mapping function * to the actually found potential variable declaration */ public boolean isTypeOnTheWay() { return isTypeOnTheWay; } @Override public void setQuery(CtQuery query) { this.query = query; } }