Skip to content

Commit

Permalink
fix TemplateTest#substituteStringLiteral
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Nov 12, 2017
1 parent 9b5ca9f commit 5ff89d4
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 36 deletions.
47 changes: 33 additions & 14 deletions src/main/java/spoon/pattern/PatternBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import spoon.SpoonException;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.code.CtReturn;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtVariableAccess;
Expand Down Expand Up @@ -75,7 +76,13 @@ public static PatternBuilder createPattern(CtElement element) {
* @return {@link PatternBuilder}
*/
public static PatternBuilder createTypePattern(CtType<?> type, Consumer<TypeView> modelSelector) {
TypeView tv = new TypeView(type.clone());
CtType<?> clonedType = type.clone();
//I am not sure yet, whether template model will need to know it's context
//- e.g. to resolve variables (which are not part of template model) from references (in template model)
// if (type.isParentInitialized()) {
// clonedType.setParent(type.getParent());
// }
TypeView tv = new TypeView(clonedType);
modelSelector.accept(tv);
return createPattern(tv.template);
}
Expand Down Expand Up @@ -176,12 +183,6 @@ public static PatternBuilder createExpressionPattern(CtType<?> type, Filter<? su
}

public Pattern build() {
if (built) {
throw new SpoonException("Method build() can be called only once");
}
// generateAutomaticParameters();

built = true;
return pattern;
}

Expand Down Expand Up @@ -305,9 +306,11 @@ public PatternBuilder configureParameters(Consumer<ParametersBuilder> parameters
/**
* adds all standard Template parameters based on {@link TemplateParameter} and {@link Parameter} annotation
* @param templateType the CtType which contains template parameters
* @param templateParameters parameters, which will be used in substitution. It is needed here,
* because parameter value types influences which AST nodes will be the target of substitution
* @return this to support fluent API
*/
public PatternBuilder configureTemplateParameters(CtType<?> templateType) {
public PatternBuilder configureTemplateParameters(CtType<?> templateType, Map<String, Object> templateParameters) {
@SuppressWarnings("rawtypes")
CtTypeReference<TemplateParameter> templateParamRef = templateType.getFactory().Type().createReference(TemplateParameter.class);
CtTypeReference<CtTypeReference> typeReferenceRef = templateType.getFactory().Type().createReference(CtTypeReference.class);
Expand All @@ -322,12 +325,10 @@ public PatternBuilder configureTemplateParameters(CtType<?> templateType) {
* Use it as Pattern parameter
*/
String parameterName = typeMember.getSimpleName();
String stringMarker = parameterName;
if (param.value() != null && param.value().length() > 0) {
stringMarker = param.value();
//for the compatibility reasons with Parameters.getNamesToValues(), use the proxy name as parameter name
parameterName = stringMarker;
}
String stringMarker = (param.value() != null && param.value().length() > 0) ? param.value() : parameterName;
//for the compatibility reasons with Parameters.getNamesToValues(), use the proxy name as parameter name
parameterName = stringMarker;

CtTypeReference<?> paramType = paramField.getType();
if (paramType.isSubtypeOf(typeReferenceRef) || paramType.getQualifiedName().equals(Class.class.getName())) {
/*
Expand All @@ -354,6 +355,24 @@ public PatternBuilder configureTemplateParameters(CtType<?> templateType) {
//all occurrences of parameter name in pattern model are subject of substitution
.byVariableReference(paramField);
}
if (paramType.getQualifiedName().equals(Object.class.getName()) && templateParameters != null) {
//if the parameter type is Object, then detect the real parameter type from the parameter value
Object value = templateParameters.get(parameterName);
if (value instanceof CtLiteral || value instanceof CtTypeReference) {
/*
* the real parameter value is CtLiteral or CtTypeReference
* We should replace all method invocations whose name equals to stringMarker
* by that CtLiteral or qualified name of CtTypeReference
*/
ParameterInfo pi = params.parameter(parameterName).getCurrentParameter();
pattern.getModel().filterChildren((CtInvocation inv) -> {
return inv.getExecutable().getSimpleName().equals(stringMarker);
}).forEach((CtInvocation inv) -> {
pattern.addSubstitutionRequest(pi, inv);
});
}
}

//any value can be converted to String. Substitute content of all string attributes
params.parameter(parameterName)
.bySubstring(stringMarker);
Expand Down
44 changes: 33 additions & 11 deletions src/main/java/spoon/pattern/TemplateBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package spoon.pattern;


import java.util.List;
import java.util.Map;

import spoon.SpoonException;
Expand All @@ -37,15 +38,27 @@
*/
public class TemplateBuilder {

@SuppressWarnings("unchecked")
public static TemplateBuilder createPattern(CtElement templateRoot) {
/**
* Creates a {@link Pattern} from {@link Template}
* @param templateRoot the root element of {@link Template} model
* @param template a instance of the {@link Template}. It is needed here,
* because parameter value types influences which AST nodes will be the target of substitution
* @return {@link TemplateBuilder}
*/
public static TemplateBuilder createPattern(CtElement templateRoot, Template<?> template) {
Factory f = templateRoot.getFactory();

CtClass<? extends Template<?>> templateType = getTemplateType(templateRoot);

if (templateType == null) {
throw new SpoonException("Cannot create a Pattern based on Template from the AST tree which is not part of Template");
}
if (template == null) {
throw new SpoonException("Cannot create a Pattern based on Template without knowing actual Template parameters");
}
if (templateType.getQualifiedName().equals(template.getClass().getName()) == false) {
throw new SpoonException("Unexpected template instance " + template.getClass().getName() + ". Expects " + templateType.getQualifiedName());
}

PatternBuilder pb;

Expand Down Expand Up @@ -76,8 +89,9 @@ public static TemplateBuilder createPattern(CtElement templateRoot) {
} else {
pb = PatternBuilder.createPattern(templateRoot);
}
pb.configureTemplateParameters(templateType);
return new TemplateBuilder(templateType, pb);
Map<String, Object> templateParameters = template == null ? null : Parameters.getTemplateParametersAsMap(f, null, template);
pb.configureTemplateParameters(templateType, templateParameters);
return new TemplateBuilder(templateType, pb, template);
}

private static CtClass<? extends Template<?>> getTemplateType(CtElement element) {
Expand All @@ -93,10 +107,12 @@ private static CtClass<? extends Template<?>> getTemplateType(CtElement element)
}
}

private Template<?> template;
private PatternBuilder patternBuilder;
private CtClass<? extends Template<?>> templateType;

private TemplateBuilder(CtClass<? extends Template<?>> templateType, PatternBuilder patternBuilder) {
private TemplateBuilder(CtClass<? extends Template<?>> templateType, PatternBuilder patternBuilder, Template<?> template) {
this.template = template;
this.patternBuilder = patternBuilder;
this.templateType = templateType;
}
Expand All @@ -109,19 +125,25 @@ public Pattern build() {
* @param template the instance of the Template of this TemplateBuilder
* @return Map of template parameters from `template`
*/
public Map<String, Object> getTemplateParameters(Template<?> template) {
return getTemplateParameters(template, null);
public Map<String, Object> getTemplateParameters() {
return getTemplateParameters(null);
}
/**
* @param template the instance of the Template of this TemplateBuilder
* @param targetType the type which will receive the model generated using returned parameters
* @return Map of template parameters from `template`
*/
public Map<String, Object> getTemplateParameters(Template<?> template, CtType<?> targetType) {
public Map<String, Object> getTemplateParameters(CtType<?> targetType) {
Factory f = templateType.getFactory();
if (templateType.getQualifiedName().equals(template.getClass().getName()) == false) {
throw new SpoonException("Unexpected template instance " + template.getClass().getName() + ". Expects " + templateType.getQualifiedName());
}
return Parameters.getTemplateParametersAsMap(f, targetType, template);
}

/**
* generates a new AST made by cloning of `patternModel` and by substitution of parameters by their values
* @param targetType the CtType, which will receive the result of substitution
* @return
*/
public <T extends CtElement> List<T> substitute(CtType<?> targetType) {
return build().substitute(getTemplateParameters(targetType));
}
}
3 changes: 1 addition & 2 deletions src/main/java/spoon/template/BlockTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ public BlockTemplate() {

public CtBlock<?> apply(CtType<?> targetType) {
CtClass<? extends BlockTemplate> c = Substitution.getTemplateCtClass(targetType, this);
TemplateBuilder tb = TemplateBuilder.createPattern(getBlock(c));
List<CtBlock<?>> blocks = tb.build().substitute(tb.getTemplateParameters(this, targetType));
List<CtBlock<?>> blocks = TemplateBuilder.createPattern(getBlock(c), this).substitute(targetType);
if (blocks.size() > 1) {
throw new SpoonException("BlockTemplate cannot return more then one block");
}
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/spoon/template/ExpressionTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ public ExpressionTemplate() {
@SuppressWarnings("unchecked")
public CtExpression<T> apply(CtType<?> targetType) {
CtClass<? extends ExpressionTemplate<?>> c = Substitution.getTemplateCtClass(targetType, this);
TemplateBuilder tb = TemplateBuilder.createPattern(getExpressionBlock(c));
List<CtBlock<?>> blocks = tb.build().substitute(tb.getTemplateParameters(this, targetType));
List<CtBlock<?>> blocks = TemplateBuilder.createPattern(getExpressionBlock(c), this).substitute(targetType);
if (blocks.size() > 1) {
throw new SpoonException("BlockTemplate cannot return more then one block");
}
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/spoon/template/StatementTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ public CtStatement apply(CtType<?> targetType) {
CtClass<?> c = Substitution.getTemplateCtClass(targetType, this);
// we substitute the first statement of method statement
CtStatement patternModel = c.getMethod("statement").getBody().getStatements().get(0);
TemplateBuilder tb = TemplateBuilder.createPattern(patternModel);
List<CtStatement> statements = tb.build().substitute(tb.getTemplateParameters(this, targetType));
List<CtStatement> statements = TemplateBuilder.createPattern(patternModel, this).substitute(targetType);
if (statements.size() > 1) {
throw new SpoonException("StatementTemplate cannot return more then one statement");
}
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/spoon/template/Substitution.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public static <T extends CtType<?>> T createTypeFromTemplate(String qualifiedTyp
CtPackage targetPackage = f.Package().getOrCreate(typeRef.getPackage().getSimpleName());
final Map<String, Object> extendedParams = new HashMap<String, Object>(templateParameters);
extendedParams.put(templateOfType.getSimpleName(), typeRef);
List<CtType<?>> generated = PatternBuilder.createPattern(templateOfType).configureTemplateParameters(templateOfType).build().substitute(extendedParams);
List<CtType<?>> generated = PatternBuilder.createPattern(templateOfType).configureTemplateParameters(templateOfType, extendedParams).build().substitute(extendedParams);
for (CtType<?> ctType : generated) {
targetPackage.addType(ctType);
}
Expand Down Expand Up @@ -533,8 +533,7 @@ public static <E extends CtElement> E substitute(CtType<?> targetType, Template<
if (targetType == null) {
throw new RuntimeException("target is null in substitution");
}
TemplateBuilder tb = TemplateBuilder.createPattern(code);
List<E> results = tb.build().substitute(tb.getTemplateParameters(template, targetType));
List<E> results = TemplateBuilder.createPattern(code, template).substitute(targetType);
if (results.size() > 1) {
throw new SpoonException("StatementTemplate cannot return more then one statement");
}
Expand Down Expand Up @@ -583,8 +582,7 @@ public static <E extends CtElement> E substitute(CtType<?> targetType, Template<
*/
public static <T extends CtType<?>> T substitute(Template<?> template, T templateType) {
// result.setParent(templateType.getParent());
TemplateBuilder tb = TemplateBuilder.createPattern(templateType);
List<T> types = tb.build().substitute(tb.getTemplateParameters(template));
List<T> types = TemplateBuilder.createPattern(templateType, template).substitute(null);
if (types.size() > 1) {
throw new SpoonException("Cannot generate more then one type");
}
Expand Down

0 comments on commit 5ff89d4

Please sign in to comment.