Parameters

From cdd
Jump to: navigation, search

Override parameters

In CDD, parameters in the chained creator and builders are generated based on the state definition. The previous example in the Creator section had this state definition:

public class PersonState {
    public int ssn;
    public String name;
}


...and a creator:

public class PersonCreator implements PersonCreatorMaker {
    private final PersonMutableState state;
 
    public Person asPerson() {
        return new Person(state.asImmutable());
    }
 
    public TextPerson asTextPerson() {
        return new TextPerson(state);
    }
    ...
}


Scroll down the creator till you found the factory:

    // --- Factory ----
 
    public class PersonFactory {
 
        public _Name ssn(int ssn) {
            return new _Ssn().ssn(ssn);
        }
 
        // ssn
        public class _Ssn {
            public _Name ssn(int ssn) {
                state.ssn = ssn;
                return new _Name();
            }
        }
 
        // name
        public class _Name {
            public PersonCreator name(String name) {
                state.name = name;
                return new PersonCreator(state);
            }
        }
    }


...the first method ssn(int ssn) is the start of the chained creator, followed by the classes _Ssn and _Name (one per attribute in the state definition). The call to PersonCreator.createPerson(), or just createPerson() if you use static imports, returns an instance of PersonFactory and is the reason you can write:

createPerson().ssn(123).name("Carl").asPerson()


Overloaded parameters

CDD supports overloading of parameters as a way to extend builders and the chained creator. Let's do that by adding a new method to the _Name class:

// name
public class _Name {
    public PersonCreator name(String name) {
        state.name = name;
        return new PersonCreator(state);
    }
 
    // add
    public PersonCreator name(String firstName, String surname) {
        state.name = firstName + " " + surname;
        return new PersonCreator(state);
    }
}


Make sure you include the "// add" comment and regenerate. Look at the top of PersonCreator and you will find this comment:

    // ===== Generated code =====
 
    /*
import net.sf.laja.cdd.annotation.Parameter;
import net.sf.laja.cdd.annotation.Parameters;
 
    @Parameters({
        @Parameter(attribute = "name", methodSignature = "String firstName, String surname", value = "firstName + " " + surname")
    })
    */


The "// add" comment was a directive to tell CDD to generate the comment.

Now insert the @Parameters statement at the top of the creator:

@Creator
public class PersonCreator implements PersonCreatorMaker {
    private final PersonMutableState state;
 
    public Person asPerson() {
        return new Person(state.asImmutable());
    }
 
    public TextPerson asTextPerson() {
        return new TextPerson(state);
    }
 
    @Parameters({
        @Parameter(attribute = "name", methodSignature = "String firstName, String surname", value = "firstName + \" \" + surname")
    })
 
    // ===== Generated code =====
 
    ...
}


You need to help CDD a little in this example (or just copy the code above) by inserting a backslash in front of the quotation marks.


The next time we generate, our new overloaded name method will be generated based on the @Parameter definition:

// name
public class _Name {
    public PersonCreator name(String name) {
        state.name = name;
        return new PersonCreator(state);
    }
 
    public PersonCreator name(String firstName, String surname) {
        state.name = firstName + " " + surname;
        return new PersonCreator(state);
    }
}


Suppressing original parameter methods

It's possible to remove the original name parameter method by modifying the @Parameter statement and set suppressOriginal to true:

@Parameter(attribute = "name", methodSignature = "String firstName, String surname", value = "firstName + \" \" + surname", suppressOriginal = true)


If we generate and scroll down to the _Name class, we can see that the original parameter method has been removed:

// name
public class _Name {
 
    public PersonCreator name(String firstName, String surname) {
        state.name = firstName + " " + surname;
        return new PersonCreator(state);
    }
}


Change method name

It is possible to change the method name by adding the method parameter, e.g.:

@Parameter(attribute = "name", method = "names", methodSignature = "String firstName, String surname", value = "firstName + \" \" + surname")


We can now create person objects like this:

Person lisa = createPerson().ssn(123).name("Lisa Simpsons").asPerson();
Person bart = createPerson().ssn(456).names("Bart", "Simpsons").asPerson();
System.out.println(lisa);
System.out.println(bart);

output:

Person{ssn=123, name="Lisa Simpsons"}
Person{ssn=456, name="Bart Simpsons"}


Skip parameters

It's possible to skip one or several parameters. Let's show how we can skip the name parameter:

@Parameters({
    @Parameter(attribute = "ssn", method = "onlySsn", methodSignature = "int ssn", value = "ssn; state.name = \"Anonymous\";", nextAttribute = "*")
})


...will produce this parameter class when generated:

// ssn
public class _Ssn {
    public _Name ssn(int ssn) {
        state.ssn = ssn;
        return new _Name();
    }
 
    public PersonCreator onlySsn(int ssn) {
        state.ssn = ssn; state.name = "Anonymous";;
        return new PersonCreator(state);
    }
}


Now we can write:

Person lisa = createPerson().onlySsn(123).asPerson();
System.out.println(lisa);

output:

Person{ssn=123, name="Anonymous"}


The statement:

nextAttribute = "*"

...jumped to the last attribute but it's also possible to jump to a specific parameter. Name is the last parameter, so this statement will give the same result:

nextAttribute = "name"


Values with more than one statement

It can be inconvenient to have all statements in one row as we did when we added onlySsn:

value = "ssn; state.name = \"Anonymous\";"


A way to improve this is to assign the value via a method:

@Parameters({
    @Parameter(attribute = "ssn", method = "onlySsn", methodSignature = "int ssn", value = "getSsnAndSetName(ssn)", nextAttribute = "*")
})
 
private int getSsnAndSetName(int ssn) {
    state.name = "Anonymous";
    return ssn;
}


< Back          Next >