au.com.dw.testdatacapturej.builder.LineBuilder.java Source code

Java tutorial

Introduction

Here is the source code for au.com.dw.testdatacapturej.builder.LineBuilder.java

Source

/*******************************************************************************
 * Copyright () 2009, 2011, 2013 David Wong
 *
 * This file is part of TestDataCaptureJ.
 *
 * TestDataCaptureJ is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TestDataCaptureJ 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
 * GNU Afferro General Public License for more details.
 *
 * You should have received a copy of the GNU Afferro General Public License
 * along with TestDataCaptureJ.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package au.com.dw.testdatacapturej.builder;

import java.lang.reflect.Array;
import java.util.List;

import org.apache.commons.lang.WordUtils;

import au.com.dw.testdatacapturej.log.LogBuilder;
import au.com.dw.testdatacapturej.meta.ObjectInfo;
import au.com.dw.testdatacapturej.util.StringEscapeUtil;

/**
 * Rendering methods for generation of code generation lines such as constructors, setters, etc.
 *
 * @author David Wong
 *
 */
public class LineBuilder {

    /**
     * Generate a constructor line for the test data class.
     * If the class is:
     *   com.test.TestClass
     * Then should generate:
     *   com.test.TestClass testClass = new com.test.TestClass();
     * 
     * This should be used for classes that have a no-argument constructor.
     * 
     * @param builder
     * @param info The ObjectInfo for the object that requires the constructor line to be generated
     */
    public void createConstructorLine(LogBuilder builder, ObjectInfo info) {
        builder.process(info.getClassName());
        builder.append(" ");

        builder.append(info.getClassFieldName());
        builder.append(info.getClassFieldNameIndex());

        builder.append(" = new ");
        builder.process(info.getClassName());
        builder.append("();");
    }

    /**
     * Generate a parameterized constructor line for the test data class.
     * If the class is:
     *   com.test.TestClass
     * And the parameters are:
     *   "test"
     *   fieldObject1
     *   1000L
     * Then should generate:
     *   com.test.TestClass testClass = new com.test.TestClass("test", fieldObject1, 1000L);
     * 
     * @param builder
     * @param info The ObjectInfo for the object that requires the constructor line to be generated
     * @return The generated constructor line
     */
    public void createParameterizedConstructorLine(LogBuilder builder, ObjectInfo info) {

        List<String> params = info.getConstructorInfo().getConstructorParameters();

        builder.append(info.getClassName());
        builder.append(" ");

        builder.append(info.getClassFieldName());
        builder.append(info.getClassFieldNameIndex());

        builder.append(" = new ");
        builder.process(info.getClassName());
        builder.append("(");

        // log the parameters
        int size = params.size();
        for (int i = 0; i < size; i++) {
            builder.append(params.get(i));
            if (i < size - 1) {
                builder.append(", ");
            }
        }
        builder.append(");");
    }

    /**
     * Generate a constructor line for the test data class which is an array.
     * 
     * If the array is:
     *   Object[]
     * Then should generate:
     *   Object[] objectArray = new Object[10];
     *  
     * @param builder
     * @param info The ObjectInfo for the object that requires the constructor line to be generated
     */
    public void createArrayConstructorLine(LogBuilder builder, ObjectInfo info) {
        // extra info required for an array constructor as opposed to an object constructor
        Object array = info.getValue();
        String arrayType = array.getClass().getComponentType().getName();
        int initialSize = Array.getLength(array);

        //StringBuilder constructorLineBuilder = new StringBuilder();

        builder.process(arrayType);
        builder.append("[] ");

        builder.append(info.getClassFieldName());
        builder.append(info.getClassFieldNameIndex());

        builder.append(" = new ");
        builder.process(arrayType);
        builder.append("[");
        if (initialSize >= 0) {
            builder.append(initialSize);
        }
        builder.append("];");
    }

    /**
     * Generate a line for the default setter method name for a field.
     * e.g. if the field name is 'someField', will generate 'setSomeField();'.
     * 
     * @param builder
     * @param fieldName
     * @param fieldValue
     * @param literal Flag whether to use the fieldValue as is, or need to format it first
     */
    public void createSetterLine(LogBuilder builder, String fieldName, Object fieldValue, boolean literal) {
        builder.append(".");
        builder.append(getSetterMethodName(fieldName));
        builder.append("(");
        if (literal) {
            // just use the literal value of the object, should be a string representing the class field name
            builder.append(fieldValue);
        } else {
            builder.append(getAppendedClassDetail(fieldValue));
        }
        builder.append(");");
    }

    /**
     * Get the default setter method name for a field.
     * 
     * @param fieldName
     * @return
     */
    public String getSetterMethodName(String fieldName) {
        return "set" + WordUtils.capitalize(fieldName);
    }

    /**
    * Generate a line for adding an element to a collection.
    * 
    * If the collection field name is :
    *   arrayList0
    * and the value to be added is :
    *   "test"
    * Then should generate:
    *   arrayList0.add("test");
     * 
     * @param builder
     * @param classFieldName
     * @param fieldValue
     * @param literal
     */
    public void createCollectionAddLine(LogBuilder builder, String classFieldName, Object fieldValue,
            boolean literal) {
        builder.append(classFieldName);
        builder.append(".");
        builder.append("add");
        builder.append("(");
        builder.append(getInterpretedValue(fieldValue, literal));
        builder.append(");");
    }

    /**
    * Generate a line for adding an element to a collection by invoking an adder method
    * on it's enclosing class
    * 
    * If the collection field name is :
    *   arrayList0
    * and the enclosing class name is :
    *   test0
    * and the value to be added is :
    *   "test"
    * and the adder method name is :
    *   addItem
    *   
    * Then should generate:
    *   test0.addItem("test");
     * 
     * @param builder
     * @param enclosingClassName Class name for the class that contains the collection field
     * @param adderMethodName Name for the adder method, needed since there is no standard convention unlike for getters and setters
     * @param fieldValue
     * @param literal
     */
    public void createCollectionEnclosingAdderLine(LogBuilder builder, String enclosingClassName,
            String adderMethodName, Object fieldValue, boolean literal) {
        builder.append(enclosingClassName);
        builder.append(".");
        builder.append(adderMethodName);
        builder.append("(");
        builder.append(getInterpretedValue(fieldValue, literal));
        builder.append(");");
    }

    /**
    * Generate a line for assigning an element to an array.
    * 
    * If the array field name is :
    *   stringArray0
    * and the value to be assigned is :
    *   "test"
    * Then should generate:
    *   stringArray0[0] = "test";
     * 
     * @param builder
     * @param classFieldName
     * @param index
     * @param fieldValue
     * @param literal
     */
    public void createArrayAssignLine(LogBuilder builder, String classFieldName, int index, Object fieldValue,
            boolean literal) {
        builder.append(classFieldName);
        builder.append("[");
        builder.append(index);
        builder.append("]");
        builder.append(" = ");
        builder.append(getInterpretedValue(fieldValue, literal));
        builder.append(";");
    }

    /**
    * Generate a line for adding an entry to a map.
    * 
    * If the map field name is :
    *   hashMap0
    * and the value to be added is :
    *   "test"
    * and the key to be used is :
    *   "key"
    * Then should generate:
    *   hashMap0.put("key", "test");
     * 
     * @param builder
     * @param classFieldName
     * @param keyName
     * @param fieldName
     * @param keyValue
     * @param fieldValue
     * @param keyLiteral
     * @param literal
     */
    public void createMapPutLine(LogBuilder builder, String classFieldName, String keyName, String fieldName,
            Object keyValue, Object fieldValue, boolean keyLiteral, boolean literal) {
        builder.append(classFieldName);
        builder.append(".");
        builder.append("put");
        builder.append("(");
        if (keyLiteral) {
            builder.append(keyName);
        } else {
            builder.append(getInterpretedValue(keyValue, keyLiteral));
        }
        builder.append(", ");
        if (literal) {
            builder.append(fieldName);
        } else {
            builder.append(getInterpretedValue(fieldValue, literal));
        }
        builder.append(");");
    }

    /**
     * Utility method for appendClassDetail() to get String value.
     * 
     * @param value
     * @return
     */
    public String getAppendedClassDetail(Object value) {
        StringBuilder stringBuilder = new StringBuilder();
        appendClassDetail(stringBuilder, value);
        return stringBuilder.toString();
    }

    /**
     * Format a simple value based on it's type, so that it can be used in test code generation.
     * e.g. 
     * Add double quotes if the object is a String or single quotes for a char.
     * 
       * String would look like: "value"
       * char would look like: 'v'
       * Long would look like: 0L
       * 
       * Values that don't require formatting (e.g. int) should display the value unchanged.
       * 
       * @param stringBuilder
     * @param value The value to be formatted
     */
    public void appendClassDetail(StringBuilder stringBuilder, Object value) {

        // add prefixes, if required
        if (value instanceof String) {
            stringBuilder.append("\"");
        } else if (value instanceof Character) {
            stringBuilder.append("'");
        }

        // the actual value
        if (value instanceof String) {
            stringBuilder.append(escapeString((String) value));
        } else {
            stringBuilder.append(value);
        }

        // add suffixes, if required
        if (value instanceof String) {
            stringBuilder.append("\"");
        } else if (value instanceof Character) {
            stringBuilder.append("'");
        } else if (value instanceof Long) {
            stringBuilder.append("L");
        } else if (value instanceof Float) {
            stringBuilder.append("f");
        } else if (value instanceof Double) {
            stringBuilder.append("d");
        }
    }

    /**
     * Format a string by escaping some chars so that the result can be used as code.
     * - turn '\' into '\\'
     * - turn '"' into '\"'
     * e.g.
     * hello becomes "hello"
     * \d becomes \\d
     * 
     * @return
     */
    public String escapeString(String stringValue) {
        String escapedBackSlash = StringEscapeUtil.escapeBackSlash(stringValue);
        String escapedQuotes = StringEscapeUtil.escapeDoubleQuotes(escapedBackSlash);
        return escapedQuotes;
    }

    /**
     * Utility method for interpretValue() to get String value.
     * 
     * @param fieldValue
     * @param literal
     * @return
     */
    public String getInterpretedValue(Object fieldValue, boolean literal) {
        StringBuilder stringBuilder = new StringBuilder();
        interpretValue(stringBuilder, fieldValue, literal);
        return stringBuilder.toString();
    }

    /**
     * Adds any additional text formatting to the field value so that it can be interpreted as java
     * code for the particular field type.
     * e.g.
     * String's need to be surrounded by double quotes ("").
     * Float's need to have 'f' appended.
     * etc ...
     * 
     * @param stringBuilder
     * @param fieldValue
     * @param literal Flag whether to do any additional formatting or just use the field value as is.
     */
    public void interpretValue(StringBuilder stringBuilder, Object fieldValue, boolean literal) {
        if (literal) {
            // just use the literal value of the object, should be a string representing the class field name
            stringBuilder.append(fieldValue);
        } else {
            appendClassDetail(stringBuilder, fieldValue);
        }
    }

}