diff --git a/src/main/java/spoon/support/template/Parameters.java b/src/main/java/spoon/support/template/Parameters.java index 06da96599ae..ed93b9ef6c5 100644 --- a/src/main/java/spoon/support/template/Parameters.java +++ b/src/main/java/spoon/support/template/Parameters.java @@ -22,6 +22,7 @@ import spoon.reflect.code.CtLiteral; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.CtTypeMember; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtFieldReference; @@ -36,6 +37,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -69,9 +71,8 @@ public static Integer getIndex(CtExpression e) { * Gets a template field parameter value. */ public static Object getValue(Template template, String parameterName, Integer index) { - Object tparamValue = null; + Field rtField = null; try { - Field rtField = null; for (Field f : RtHelper.getAllFields(template.getClass())) { if (isParameterSource(f)) { if (parameterName.equals(getParameterName(f))) { @@ -80,6 +81,20 @@ public static Object getValue(Template template, String parameterName, Intege } } } + } catch (Exception e) { + throw new UndefinedParameterException(e); + } + Object tparamValue = getValue(template, parameterName, rtField); + if (rtField.getType().isArray() && (index != null)) { + tparamValue = ((Object[]) tparamValue)[index]; + } + return tparamValue; + } + private static Object getValue(Template template, String parameterName, Field rtField) { + if (rtField == null) { + throw new UndefinedParameterException(); + } + try { if (Modifier.isFinal(rtField.getModifiers())) { Map m = finals.get(template); if (m == null) { @@ -88,14 +103,10 @@ public static Object getValue(Template template, String parameterName, Intege return m.get(parameterName); } rtField.setAccessible(true); - tparamValue = rtField.get(template); - if (rtField.getType().isArray() && (index != null)) { - tparamValue = ((Object[]) tparamValue)[index]; - } + return rtField.get(template); } catch (Exception e) { - throw new UndefinedParameterException(); + throw new UndefinedParameterException(e); } - return tparamValue; } static Map, Map> finals = new HashMap<>(); @@ -194,6 +205,47 @@ public static List getNames(CtClass> templateType) } return params; } + /** + * Gets the Map of names to template parameter value for all the template parameters of a given template type + * (including the ones defined by the super types). + */ + public static Map getNamesToValues(Template template, CtClass> templateType) { + //use linked hash map to assure same order of parameter names. There are cases during substitution of parameters when substitution order matters. E.g. SubstitutionVisitor#substituteName(...) + Map params = new LinkedHashMap<>(); + try { + for (CtFieldReference f : templateType.getAllFields()) { + if (isParameterSource(f)) { + String parameterName = getParameterName(f); + params.put(parameterName, getValue(template, parameterName, (Field) f.getActualField())); + } + } + } catch (Exception e) { + throw new SpoonException("Getting of template parameters failed", e); + } + return params; + } + + /** + * Gets the Map of names to template parameter values for all the template parameters of a given template type + * + adds mapping of template model reference to target type as parameter too + * @param f + * the factory + * @param targetType + * the target type of the substitution (can be null), which will be done with result parameters + * @param template + * the template that holds the parameter values + */ + public static Map getTemplateParametersAsMap(Factory f, CtType targetType, Template template) { + Map params = new HashMap<>(Parameters.getNamesToValues(template, (CtClass) f.Class().get(template.getClass()))); + if (targetType != null) { + /* + * there is required to replace all template model references by target type reference. + * Handle that request as template parameter too + */ + params.put(template.getClass().getSimpleName(), targetType.getReference()); + } + return params; + } /** * Tells if a given field is a template parameter. diff --git a/src/main/java/spoon/support/template/SubstitutionVisitor.java b/src/main/java/spoon/support/template/SubstitutionVisitor.java index 60f0da47dcc..5176121e247 100644 --- a/src/main/java/spoon/support/template/SubstitutionVisitor.java +++ b/src/main/java/spoon/support/template/SubstitutionVisitor.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import spoon.SpoonException; import spoon.reflect.code.CtAbstractInvocation; @@ -36,7 +37,6 @@ import spoon.reflect.code.CtThisAccess; import spoon.reflect.code.CtVariableAccess; import spoon.reflect.declaration.CtAnnotation; -import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtNamedElement; @@ -54,6 +54,7 @@ import spoon.reflect.visitor.Query; import spoon.reflect.visitor.filter.VariableAccessFilter; import spoon.template.Local; +import spoon.template.Parameter; import spoon.template.Template; import spoon.template.TemplateParameter; @@ -89,24 +90,23 @@ public InheritanceSustitutionScanner(SubstitutionVisitor parent) { @Override public void scanCtExecutable(CtExecutable e) { // replace method parameters - for (CtParameter parameter : new ArrayList<>(e.getParameters())) { - String name = parameter.getSimpleName(); - for (String pname : parameterNames) { - if (name.equals(pname)) { - Object value = Parameters.getValue(template, pname, null); - int i = parameter.getParent().getParameters().indexOf(parameter); - if (value instanceof List) { - List l = (List) value; - for (Object p : l) { - CtParameter p2 = ((CtParameter) p).clone(); - p2.setParent(parameter.getParent()); - parameter.getParent().getParameters().add(i++, p2); - } - parameter.getParent().getParameters().remove(parameter); - } - } + List> substitutedParams = new ArrayList<>(e.getParameters().size()); + boolean wasChanged = false; + for (CtParameter parameter : e.getParameters()) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + List> list = (List) getParameterValueAsList(CtParameter.class, getParameterValue(parameter.getSimpleName())); + if (list == null) { + //it is normal parameter, keep it. + substitutedParams.add(parameter); + } else { + wasChanged = true; + //current parameter has to be substituted by a template parameter value + substitutedParams.addAll(list); } } + if (wasChanged) { + e.setParameters(substitutedParams); + } super.scanCtExecutable(e); } @@ -129,66 +129,52 @@ public void scanCtElement(CtElement e) { @Override public void scanCtNamedElement(CtNamedElement element) { if (element.getDocComment() != null) { - element.setDocComment(substituteInDocComment(element.getDocComment())); + element.setDocComment(substituteName(element.getDocComment())); } // replace parameters in names - element.setSimpleName(substituteName(element.getSimpleName(), element)); + element.setSimpleName(substituteName(element.getSimpleName())); super.scanCtNamedElement(element); } @Override public void scanCtReference(CtReference reference) { - reference.setSimpleName(substituteName(reference.getSimpleName(), reference)); + reference.setSimpleName(substituteName(reference.getSimpleName())); super.scanCtReference(reference); } - private String substituteName(String name, CtElement element) { - for (String pname : parameterNames) { + private String substituteName(String name) { + for (Map.Entry e : namesToValues.entrySet()) { + String pname = e.getKey(); if (name.contains(pname)) { - Object value = Parameters.getValue(template, pname, null); - if (value instanceof String) { - // replace with the string value - name = name.replace(pname, (String) value); - } else if ((value instanceof CtTypeReference) && (element instanceof CtType)) { - // replace with the type reference's name - name = name.replace(pname, ((CtTypeReference) value).getSimpleName()); - } + String value = getParameterValueAsString(e.getValue()); + name = name.replace(pname, value); } } return name; } - private String substituteInDocComment(String docComment) { - String result = docComment; - for (String pname : parameterNames) { - Object value = Parameters.getValue(template, pname, null); - if (value instanceof String) { - result = result.replace(pname, (String) value); - } - } - return result; - } - /** statically inline foreach */ + @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void visitCtForEach(CtForEach foreach) { if (foreach.getExpression() instanceof CtFieldAccess) { CtFieldAccess fa = (CtFieldAccess) foreach.getExpression(); - if (Parameters.isParameterSource(fa.getVariable())) { - Object[] value = (Object[]) Parameters.getValue(template, fa.getVariable().getSimpleName(), null); + Object value = getParameterValue(fa.getVariable().getSimpleName()); + if (value != null) { + List list = getParameterValueAsList(CtExpression.class, value); CtBlock l = foreach.getFactory().Core().createBlock(); CtStatement body = foreach.getBody(); - for (Object element : value) { + for (CtExpression element : list) { CtStatement b = body.clone(); for (CtVariableAccess va : Query.getElements(b, new VariableAccessFilter<>(foreach.getVariable().getReference()))) { - va.replace((CtExpression) element); + va.replace(element); } if (b instanceof CtBlock && ((CtBlock) b).getStatements().size() == 1) { b = ((CtBlock) b).getStatement(0); } l.addStatement(b); } - foreach.replace(l); + replace(foreach, l); throw new DoNotFurtherTemplateThisElement(foreach); } } @@ -205,33 +191,36 @@ public void visitCtFieldWrite(CtFieldWrite fieldWrite) { visitFieldAccess(fieldWrite); } - private void visitFieldAccess(CtFieldAccess fieldAccess) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void visitFieldAccess(final CtFieldAccess fieldAccess) { CtFieldReference ref = fieldAccess.getVariable(); if ("length".equals(ref.getSimpleName())) { if (fieldAccess.getTarget() instanceof CtFieldAccess) { ref = ((CtFieldAccess) fieldAccess.getTarget()).getVariable(); - if (Parameters.isParameterSource(ref)) { - Object[] value = (Object[]) Parameters.getValue(template, ref.getSimpleName(), null); - fieldAccess.replace((CtExpression) fieldAccess.getFactory().Code().createLiteral(value - .length)); + Object value = getParameterValue(ref.getSimpleName()); + if (value != null) { + //the items of this list are not cloned + List list = getParameterValueAsList(Object.class, value); + replace(fieldAccess, (CtExpression) fieldAccess.getFactory().Code().createLiteral(list.size())); throw new DoNotFurtherTemplateThisElement(fieldAccess); } } } - if (Parameters.isParameterSource(ref)) { + Object v = getParameterValue(ref.getSimpleName()); + if (v != null) { // replace direct field parameter accesses - Object value = Parameters.getValue(template, ref.getSimpleName(), Parameters.getIndex(fieldAccess)); + Object value = getParameterValueAtIndex(Object.class, v, Parameters.getIndex(fieldAccess)); CtExpression toReplace = fieldAccess; if (fieldAccess.getParent() instanceof CtArrayAccess) { toReplace = (CtExpression) fieldAccess.getParent(); } if (!(value instanceof TemplateParameter)) { if (value instanceof Class) { - toReplace.replace(factory.Code() + replace(toReplace, factory.Code() .createClassAccess(factory.Type().createReference(((Class) value).getName()))); } else if (value instanceof Enum) { CtTypeReference enumType = factory.Type().createReference(value.getClass()); - toReplace.replace(factory.Code().createVariableRead( + replace(toReplace, factory.Code().createVariableRead( factory.Field().createReference(enumType, enumType, ((Enum) value).name()), true)); } else if (value instanceof List) { // replace list of CtParameter for generic access to the @@ -249,12 +238,12 @@ private void visitFieldAccess(CtFieldAccess fieldAccess) { i++; } } else if ((value != null) && value.getClass().isArray()) { - toReplace.replace(factory.Code().createLiteralArray((Object[]) value)); + replace(toReplace, factory.Code().createLiteralArray((Object[]) value)); } else { - toReplace.replace(factory.Code().createLiteral(value)); + replace(toReplace, factory.Code().createLiteral(value)); } } else { - toReplace.clone(); + replace(toReplace, toReplace.clone()); } // do not visit if replaced throw new DoNotFurtherTemplateThisElement(fieldAccess); @@ -278,24 +267,22 @@ public void visitCtInvocation(CtInvocation invocation) { fa = (CtFieldAccess) ((CtArrayAccess>) invocation.getTarget()).getTarget(); } if ((fa != null) && (fa.getTarget() == null || fa.getTarget() instanceof CtThisAccess)) { - TemplateParameter tparamValue = (TemplateParameter) Parameters - .getValue(template, fa.getVariable().getSimpleName(), Parameters.getIndex(fa)); - CtCodeElement r = null; - if (tparamValue != null) { - r = ((CtCodeElement) tparamValue).clone(); + CtCodeElement r = getParameterValueAtIndex(CtCodeElement.class, + getParameterValue(fa.getVariable().getSimpleName()), Parameters.getIndex(fa)); + if (r != null) { // substitute in the replacement (for fixing type // references // and // for recursive substitution) r.accept(parent); } - if ((invocation.getParent() instanceof CtReturn) && (r instanceof CtBlock)) { + if (invocation.isParentInitialized() && (invocation.getParent() instanceof CtReturn) && (r instanceof CtBlock)) { // block template parameters in returns should // replace // the return ((CtReturn) invocation.getParent()).replace((CtStatement) r); } else { - invocation.replace(r); + replace(invocation, r); } } // do not visit the invocation if replaced @@ -309,18 +296,11 @@ public void visitCtInvocation(CtInvocation invocation) { public void scanCtExpression(CtExpression expression) { for (int i = 0; i < expression.getTypeCasts().size(); i++) { CtTypeReference t = (CtTypeReference) expression.getTypeCasts().get(i); - if (parameterNames.contains(t.getSimpleName())) { + CtTypeReference value = getParameterValueAsTypeReference(factory, getParameterValue(t.getSimpleName())); + if (value != null) { // replace type parameters // TODO: this would probably not work with inner classes!!! - Object o = Parameters.getValue(template, t.getSimpleName(), null); - if (o instanceof Class) { - t = factory.Type().createReference(((Class) o)); - } else if (o instanceof CtTypeReference) { - t = (CtTypeReference) o; - expression.getTypeCasts().set(i, t); - } else { - throw new RuntimeException("unsupported reference substitution"); - } + expression.getTypeCasts().set(i, value); } } super.scanCtExpression(expression); @@ -329,25 +309,21 @@ public void scanCtExpression(CtExpression expression) { @SuppressWarnings("unchecked") @Override public void scanCtTypedElement(CtTypedElement e) { - if ((e.getType() != null) && parameterNames.contains(e.getType().getSimpleName())) { - // replace type parameters - // TODO: this would probably not work with inner classes!!! - CtTypeReference t; - Object o = Parameters.getValue(template, e.getType().getSimpleName(), null); - if (o instanceof Class) { - o = factory.Type().createReference(((Class) o)); - } - if (o instanceof CtTypeReference) { - if ((e.getType() instanceof CtArrayTypeReference) && !(o instanceof CtArrayTypeReference)) { + final CtTypeReference typeOfE = e.getType(); + if (typeOfE != null) { + CtTypeReference o = getParameterValueAsTypeReference(factory, getParameterValue(typeOfE.getSimpleName())); + if (o != null) { + // replace type parameters + // TODO: this would probably not work with inner classes!!! + CtTypeReference t; + if ((typeOfE instanceof CtArrayTypeReference) && !(o instanceof CtArrayTypeReference)) { t = (CtArrayTypeReference) e.getFactory().Type().createArrayReference( - (CtTypeReference) o, - ((CtArrayTypeReference) e.getType()).getDimensionCount()); + o, + ((CtArrayTypeReference) typeOfE).getDimensionCount()); } else { t = (CtTypeReference) o; } e.setType(t); - } else { - throw new RuntimeException("unsupported reference substitution"); } } super.scanCtTypedElement(e); @@ -368,46 +344,19 @@ public void visitCtExecutableReference(CtExecutableReference reference) { */ @Override public void visitCtTypeReference(CtTypeReference reference) { - // if (reference.equals(templateRef) || (!reference.isPrimitif() && - // f.Type().createReference(Template.class).isAssignableFrom(reference) - // && reference.isAssignableFrom(templateRef))) { - if (reference.equals(templateRef)) { - // replace references to the template type with references - // to the targetType (only if the referenced element exists - // in the target) - reference.setDeclaringType(targetRef.getDeclaringType()); - reference.setPackage(targetRef.getPackage()); - reference.setSimpleName(targetRef.getSimpleName()); - } - if (parameterNames.contains(reference.getSimpleName())) { + Object o = getParameterValue(reference.getSimpleName()); + if (o != null) { // replace type parameters // TODO: this would probably not work with inner classes!!! - CtTypeReference t; - Object o = Parameters.getValue(template, reference.getSimpleName(), null); - if (o instanceof Class) { - t = factory.Type().createReference(((Class) o)); - } else if (o instanceof CtTypeReference) { - t = ((CtTypeReference) o).clone(); + boolean paramHasActualTypeArguments = o instanceof CtTypeReference; + CtTypeReference t = getParameterValueAsTypeReference(factory, o); + if (paramHasActualTypeArguments) { + //the origin parameter has actual type arguments, apply them reference.setActualTypeArguments(t.getActualTypeArguments()); - } else { - throw new RuntimeException( - "unsupported reference substitution: " + reference.getSimpleName() + " with value " + o); } reference.setPackage(t.getPackage()); reference.setSimpleName(t.getSimpleName()); reference.setDeclaringType(t.getDeclaringType()); - } else if (templateTypeRef.isSubtypeOf(reference)) { - // this can only be a template inheritance case (to be verified) - CtTypeReference sc = targetRef.getSuperclass(); - if (sc != null) { - reference.setDeclaringType(sc.getDeclaringType()); - reference.setPackage(sc.getPackage()); - reference.setSimpleName(sc.getSimpleName()); - } else { - reference.setDeclaringType(null); - reference.setPackage(factory.Package().createReference("java.lang")); - reference.setSimpleName("Object"); - } } super.visitCtTypeReference(reference); } @@ -419,47 +368,46 @@ public void visitCtTypeReference(CtTypeReference reference) { CtExecutableReference S; - CtTypeReference targetRef; - - CtType targetType; - - Template template; - - CtTypeReference templateRef; - - CtTypeReference