All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spoon.pattern.InlinedStatementConfigurator Maven / Gradle / Ivy

Go to download

Spoon is a tool for meta-programming, analysis and transformation of Java programs.

There is a newer version: 11.1.1-beta-14
Show newest version
/*
 * SPDX-License-Identifier: (MIT OR CECILL-C)
 *
 * Copyright (C) 2006-2023 INRIA and contributors
 *
 * Spoon is available either under the terms of the MIT License (see LICENSE-MIT.txt) or the Cecill-C License (see LICENSE-CECILL-C.txt). You as the user are entitled to choose the terms under which to adopt Spoon.
 */
package spoon.pattern;

import static spoon.pattern.PatternBuilder.bodyToStatements;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

import spoon.SpoonException;
import spoon.pattern.internal.node.ForEachNode;
import spoon.pattern.internal.node.ListOfNodes;
import spoon.pattern.internal.node.ParameterNode;
import spoon.pattern.internal.node.PrimitiveMatcher;
import spoon.pattern.internal.node.RootNode;
import spoon.pattern.internal.node.SwitchNode;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtForEach;
import spoon.reflect.code.CtIf;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.code.CtStatement;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.CtAbstractVisitor;
import spoon.support.Experimental;

/**
 * Builds inline statements of Pattern
 *
 * For example if the `for` statement in this pattern model
 * 

 * for(Object x : $iterable$) {
 *	System.out.println(x);
 * }
 * 
* is configured as inline statement and a Pattern is substituted * using parameter $iterable$ = new String[]{"A", "B", "C"} * then pattern generated this code *

 * System.out.println("A");
 * System.out.println("B");
 * System.out.println("C");
 * 
* because inline statements are executed during substitution process and are not included in generated result. * * Main documentation at http://spoon.gforge.inria.fr/pattern.html. */ @Experimental public class InlinedStatementConfigurator { private final PatternBuilder patternBuilder; private boolean failOnMissingParameter = true; private ConflictResolutionMode conflictResolutionMode = ConflictResolutionMode.FAIL; public InlinedStatementConfigurator(PatternBuilder patternBuilder) { this.patternBuilder = patternBuilder; } /** * @return current {@link ConflictResolutionMode} */ public ConflictResolutionMode getConflictResolutionMode() { return conflictResolutionMode; } /** * Defines what happens when before explicitly added {@link RootNode} has to be replaced by another {@link RootNode} * @param conflictResolutionMode to be applied mode * @return this to support fluent API */ public InlinedStatementConfigurator setConflictResolutionMode(ConflictResolutionMode conflictResolutionMode) { this.conflictResolutionMode = conflictResolutionMode; return this; } /** * marks all CtIf and CtForEach whose expression contains a variable reference named `variableName` as inline statement. * @param variableName to be searched variable name * @return this to support fluent API */ public InlinedStatementConfigurator inlineIfOrForeachReferringTo(String variableName) { patternBuilder.patternQuery .filterChildren((CtVariableReference varRef) -> variableName.equals(varRef.getSimpleName())) .forEach(this::byElement); return this; } /** * marks all CtIf and CtForEach whose expression contains element as inline statement. * @param element a child of CtIf or CtForEach * @return this to support fluent API */ InlinedStatementConfigurator byElement(CtElement element) { CtStatement stmt = element instanceof CtStatement ? (CtStatement) element : element.getParent(CtStatement.class); //called for first parent statement of all current parameter substitutions stmt.accept(new CtAbstractVisitor() { @Override public void visitCtForEach(CtForEach foreach) { markAsInlined(foreach); } @Override public void visitCtIf(CtIf ifElement) { markAsInlined(ifElement); } }); return this; } /** * marks {@link CtForEach} as inline statement. * @param foreach to be marked {@link CtForEach} element * @return this to support fluent API */ public InlinedStatementConfigurator markAsInlined(CtForEach foreach) { //detect meta elements by different way - e.g. comments? RootNode vr = patternBuilder.getPatternNode(foreach.getExpression()); if (!(vr instanceof PrimitiveMatcher)) { throw new SpoonException("Each inline `for(x : iterable)` statement must have defined pattern parameter for `iterable` expression"); } PrimitiveMatcher parameterOfExpression = (PrimitiveMatcher) vr; ForEachNode mvr = new ForEachNode(); mvr.setIterableParameter(parameterOfExpression); CtLocalVariable lv = foreach.getVariable(); //create locally unique name of this local parameter String paramName = lv.getSimpleName(); patternBuilder.configureLocalParameters(pb -> { pb.parameter(paramName).byVariable(lv); mvr.setLocalParameter(pb.getCurrentParameter()); }); mvr.setNestedModel(patternBuilder.getPatternNode(foreach, CtRole.BODY, CtRole.STATEMENT)); /* * create Substitution request for whole `foreach`, * resolve the expressions at substitution time * and substitute the body of `foreach` as subpattern * 0 or more times - once for each value of Iterable expression. */ patternBuilder.setNodeOfElement(foreach, mvr, conflictResolutionMode); return this; } /** * marks {@link CtIf} as inline statement. * @param ifElement to be marked {@link CtIf} element * @return this to support fluent API */ public InlinedStatementConfigurator markAsInlined(CtIf ifElement) { SwitchNode osp = new SwitchNode(); boolean[] canBeInline = { true }; forEachIfCase(ifElement, (expression, block) -> { //detect meta elements by different way - e.g. comments? if (expression != null) { //expression is not null, it is: if(expression) {} RootNode vrOfExpression = patternBuilder.getPatternNode(expression); if (!(vrOfExpression instanceof ParameterNode)) { if (failOnMissingParameter) { throw new SpoonException("Each inline `if` statement must have defined pattern parameter in expression. If you want to ignore this, then call InlinedStatementConfigurator#setFailOnMissingParameter(false) first."); } else { canBeInline[0] = false; return; } } if (vrOfExpression instanceof PrimitiveMatcher) { osp.addCase((PrimitiveMatcher) vrOfExpression, getPatternNode(bodyToStatements(block))); } else { throw new SpoonException("Inline `if` statement have defined single value pattern parameter in expression. But there is " + vrOfExpression.getClass().getName()); } } else { //expression is null, it is: else {} osp.addCase(null, getPatternNode(bodyToStatements(block))); } }); if (canBeInline[0]) { /* * create Substitution request for whole `if`, * resolve the expressions at substitution time and substitute only the `if` then/else statements, not `if` itself. */ patternBuilder.setNodeOfElement(ifElement, osp, conflictResolutionMode); } return this; } private ListOfNodes getPatternNode(List template) { List nodes = new ArrayList<>(template.size()); for (CtElement element : template) { nodes.add(patternBuilder.getPatternNode(element)); } return new ListOfNodes(nodes); } /** * calls function once for each expression/then block and at the end calls function for last else block. * * @param ifElement * @param consumer * @return true if all function calls returns true or if there is no function call */ private void forEachIfCase(CtIf ifElement, BiConsumer, CtStatement> consumer) { consumer.accept(ifElement.getCondition(), ifElement.getThenStatement()); CtStatement elseStmt = getElseIfStatement(ifElement.getElseStatement()); if (elseStmt instanceof CtIf) { //another else if case forEachIfCase((CtIf) elseStmt, consumer); } else if (elseStmt != null) { //last else consumer.accept(null, elseStmt); } } private CtStatement getElseIfStatement(CtStatement elseStmt) { if (elseStmt instanceof CtBlock) { CtBlock block = (CtBlock) elseStmt; if (block.isImplicit()) { List stmts = block.getStatements(); if (stmts.size() == 1) { if (stmts.get(0) instanceof CtIf) { return stmts.get(0); } } } } return elseStmt; } public boolean isFailOnMissingParameter() { return failOnMissingParameter; } /** * @param failOnMissingParameter set true if it should fail when some statement cannot be handled as inline * set false if ssuch statement should be kept as part of template. * @return this to support fluent API */ public InlinedStatementConfigurator setFailOnMissingParameter(boolean failOnMissingParameter) { this.failOnMissingParameter = failOnMissingParameter; return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy