/**
* 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.support.template;
import spoon.SpoonException;
import spoon.reflect.code.CtArrayAccess;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.support.util.RtHelper;
import spoon.template.Parameter;
import spoon.template.Template;
import spoon.template.TemplateParameter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class defines an API to manipulate template parameters.
*/
public abstract class Parameters {
private Parameters() {
}
/**
* The prefix "_FIELD_" for a parameter that represents a fields in order to
* avoid name clashes.
*/
protected static final String fieldPrefix = "_FIELD_";
/**
* Gets the index of a one-dimension array (helper).
*/
@SuppressWarnings("unchecked")
public static Integer getIndex(CtExpression<?> e) {
if (e.getParent() instanceof CtArrayAccess) {
CtExpression<Integer> indexExpression = ((CtArrayAccess<?, CtExpression<Integer>>) e.getParent()).getIndexExpression();
return ((CtLiteral<Integer>) indexExpression).getValue();
}
return null;
}
/**
* Gets a template field parameter value.
*/
public static Object getValue(Template<?> template, String parameterName, Integer index) {
Object tparamValue = null;
try {
Field rtField = null;
for (Field f : RtHelper.getAllFields(template.getClass())) {
if (isParameterSource(f)) {
if (parameterName.equals(getParameterName(f))) {
rtField = f;
break;
}
}
}
if (Modifier.isFinal(rtField.getModifiers())) {
Map<String, Object> m = finals.get(template);
if (m == null) {
return null;
}
return m.get(parameterName);
}
rtField.setAccessible(true);
tparamValue = rtField.get(template);
if (rtField.getType().isArray() && (index != null)) {
tparamValue = ((Object[]) tparamValue)[index];
}
} catch (Exception e) {
throw new UndefinedParameterException();
}
return tparamValue;
}
static Map<Template<?>, Map<String, Object>> finals = new HashMap<>();
public static CtField<?> getParameterField(CtClass<? extends Template<?>> templateClass, String parameterName) {
for (CtTypeMember typeMember : templateClass.getTypeMembers()) {
if (!(typeMember instanceof CtField)) {
continue;
}
CtField<?> f = (CtField<?>) typeMember;
Parameter p = f.getAnnotation(Parameter.class);
if (p == null) {
continue;
}
if (f.getSimpleName().equals(parameterName)) {
return f;
}
if (parameterName.equals(p.value())) {
return f;
}
}
return null;
}
/**
* Sets a template field parameter value.
*/
@SuppressWarnings("null")
public static void setValue(Template<?> template, String parameterName, Integer index, Object value) {
Object tparamValue = null;
try {
Field rtField = null;
for (Field f : RtHelper.getAllFields(template.getClass())) {
if (isParameterSource(f)) {
if (parameterName.equals(getParameterName(f))) {
rtField = f;
break;
}
}
}
if (rtField == null) {
return;
}
if (Modifier.isFinal(rtField.getModifiers())) {
Map<String, Object> m = finals.get(template);
if (m == null) {
finals.put(template, m = new HashMap<>());
}
m.put(parameterName, value);
return;
}
rtField.setAccessible(true);
rtField.set(template, value);
if (rtField.getType().isArray()) {
// TODO: RP: THIS IS WRONG!!!! tparamValue is never used or
// set!!
tparamValue = ((Object[]) tparamValue)[index];
}
} catch (Exception e) {
throw new UndefinedParameterException();
}
}
private static String getParameterName(Field f) {
String name = f.getName();
Parameter p = f.getAnnotation(Parameter.class);
if ((p != null) && !p.value().equals("")) {
name = p.value();
}
return name;
}
private static String getParameterName(CtFieldReference<?> f) {
String name = f.getSimpleName();
Parameter p = f.getDeclaration().getAnnotation(Parameter.class);
if ((p != null) && !p.value().equals("")) {
name = p.value();
}
return name;
}
/**
* Gets the names of all the template parameters of a given template type
* (including the ones defined by the super types).
*/
public static List<String> getNames(CtClass<? extends Template<?>> templateType) {
List<String> params = new ArrayList<>();
try {
for (CtFieldReference<?> f : templateType.getReference().getAllFields()) {
if (isParameterSource(f)) {
params.add(getParameterName(f));
}
}
} catch (Exception e) {
throw new SpoonException("Getting of template parameters failed", e);
}
return params;
}
/**
* Tells if a given field is a template parameter.
*/
public static boolean isParameterSource(CtFieldReference<?> ref) {
CtField<?> field = ref.getDeclaration();
if (field == null) {
// we must have the source of this fieldref, otherwise we cannot use it as template parameter
return false;
}
if (field.getAnnotation(Parameter.class) != null) {
//it is the template field which represents template parameter, because of "Parameter" annotation
return true;
}
if (ref.getType() instanceof CtTypeParameterReference) {
//the template fields, which are using generic type like <T>, are not template parameters
return false;
}
if (ref.getSimpleName().equals("this")) {
//the reference to this is not template parameter
return false;
}
if (getTemplateParameterType(ref.getFactory()).isSubtypeOf(ref.getType())) {
//the type of template field is or extends from class TemplateParameter.
return true;
}
return false;
}
/**
* Tells if a given field is a template parameter.
*/
public static boolean isParameterSource(Field field) {
return (field.getAnnotation(Parameter.class) != null) || TemplateParameter.class.isAssignableFrom(field.getType());
}
static CtTypeReference<TemplateParameter<?>> templateParameterType;
@SuppressWarnings({ "rawtypes", "unchecked" })
private static synchronized CtTypeReference<TemplateParameter<?>> getTemplateParameterType(Factory factory) {
if (templateParameterType == null) {
templateParameterType = (CtTypeReference) factory.Type().createReference(TemplateParameter.class);
}
return templateParameterType;
}
/**
* Creates an empty template parameter of the <code>T</code> type where
* {@link TemplateParameter#S()} does not return <code>null</code> in case
* the template code needs to be executed such as in static initializers.
*/
@SuppressWarnings("unchecked")
public static <T> TemplateParameter<T> NIL(Class<? extends T> type) {
if (Number.class.isAssignableFrom(type)) {
return (TemplateParameter<T>) new TemplateParameter<Number>() {
public Number S() {
return 0;
}
};
}
return new TemplateParameter<T>() {
public T S() {
return null;
}
};
}
/**
* returns all the runtime fields of a template representing a template parameter
*/
public static List<Field> getAllTemplateParameterFields(Class<? extends Template> clazz) {
if (!Template.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException();
}
List<Field> result = new ArrayList<>();
for (Field f : RtHelper.getAllFields(clazz)) {
if (isParameterSource(f)) {
result.add(f);
}
}
return result;
}
/**
* returns all the compile_time fields of a template representing a template parameter
*/
public static List<CtField<?>> getAllTemplateParameterFields(Class<? extends Template<?>> clazz, Factory factory) {
CtClass<?> c = factory.Class().get(clazz);
if (c == null) {
throw new IllegalArgumentException("Template not in template classpath");
}
List<CtField<?>> result = new ArrayList<>();
for (Field f : getAllTemplateParameterFields(clazz)) {
result.add(c.getField(f.getName()));
}
return result;
}
}