* @param classNode which contains postconditions, so an old variable generating method makes sense here.
*/
public static void addOldVariableMethodNode(final ClassNode classNode) {
if (classNode.getDeclaredMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) return;
final BlockStatement methodBlockStatement = new BlockStatement();
final MapExpression oldVariablesMap = new MapExpression();
// create variable assignments for old variables
for (final FieldNode fieldNode : classNode.getFields()) {
if (fieldNode.getName().startsWith("$")) continue;
final ClassNode fieldType = ClassHelper.getWrapper(fieldNode.getType());
if (fieldType.getName().startsWith("java.lang") || ClassHelper.isPrimitiveType(fieldType) || fieldType.getName().startsWith("java.math") ||
fieldType.getName().startsWith("java.util") ||
fieldType.getName().startsWith("java.sql") ||
fieldType.getName().equals("groovy.lang.GString") ||
fieldType.getName().equals("java.lang.String")) {
MethodNode cloneMethod = fieldType.getMethod("clone", Parameter.EMPTY_ARRAY);
// if a clone classNode is available, the value is cloned
if (cloneMethod != null && fieldType.implementsInterface(ClassHelper.make("java.lang.Cloneable"))) {
VariableExpression oldVariable = new VariableExpression("$old$" + fieldNode.getName(), fieldNode.getType());
oldVariable.setAccessedVariable(oldVariable);
final MethodCallExpression methodCall = new MethodCallExpression(new FieldExpression(fieldNode), "clone", ArgumentListExpression.EMPTY_ARGUMENTS);
// return null if field is null
methodCall.setSafe(true);
ExpressionStatement oldVariableAssignment = new ExpressionStatement(
new DeclarationExpression(oldVariable,
Token.newSymbol(Types.ASSIGN, -1, -1),
methodCall));
methodBlockStatement.addStatement(oldVariableAssignment);
oldVariablesMap.addMapEntryExpression(new MapEntryExpression(new ConstantExpression(oldVariable.getName().substring("$old$".length())), oldVariable));
} else if (ClassHelper.isPrimitiveType(fieldType)
|| ClassHelper.isNumberType(fieldType)
|| fieldType.getName().startsWith("java.math")
|| fieldType.getName().equals("groovy.lang.GString")
|| fieldType.getName().equals("java.lang.String")) {
VariableExpression oldVariable = new VariableExpression("$old$" + fieldNode.getName(), fieldNode.getType());
oldVariable.setAccessedVariable(oldVariable);
ExpressionStatement oldVariableAssignment = new ExpressionStatement(
new DeclarationExpression(oldVariable,
Token.newSymbol(Types.ASSIGN, -1, -1),
new FieldExpression(fieldNode)));
methodBlockStatement.addStatement(oldVariableAssignment);
oldVariablesMap.addMapEntryExpression(new MapEntryExpression(new ConstantExpression(oldVariable.getName().substring("$old$".length())), oldVariable));
}
}
}
VariableExpression oldVariable = new VariableExpression("old", new ClassNode(Map.class));
oldVariable.setAccessedVariable(oldVariable);
ExpressionStatement oldVariabeStatement = new ExpressionStatement(
new DeclarationExpression(oldVariable,
Token.newSymbol(Types.ASSIGN, -1, -1),
oldVariablesMap));
methodBlockStatement.addStatement(oldVariabeStatement);
VariableExpression mergedOldVariables = null;
// let's ask the super class for old variables...
if (classNode.getSuperClass() != null && classNode.getSuperClass().getMethod(OLD_VARIABLES_METHOD, Parameter.EMPTY_ARRAY) != null) {
mergedOldVariables = new VariableExpression("mergedOldVariables", new ClassNode(Map.class));
mergedOldVariables.setAccessedVariable(mergedOldVariables);
ExpressionStatement mergedOldVariablesStatement = new ExpressionStatement(
new DeclarationExpression(mergedOldVariables,
Token.newSymbol(Types.ASSIGN, -1, -1),
new MethodCallExpression(oldVariable, "plus", new ArgumentListExpression(new MethodCallExpression(VariableExpression.SUPER_EXPRESSION, OLD_VARIABLES_METHOD, ArgumentListExpression.EMPTY_ARGUMENTS)))));
methodBlockStatement.addStatement(mergedOldVariablesStatement);
}
methodBlockStatement.addStatement(new ReturnStatement(mergedOldVariables != null ? mergedOldVariables : oldVariable));
final MethodNode preconditionMethodNode = classNode.addMethod(OLD_VARIABLES_METHOD, Opcodes.ACC_PROTECTED, new ClassNode(Map.class), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, methodBlockStatement);
preconditionMethodNode.setSynthetic(true);
}