package org.springframework.roo.classpath.layers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.classpath.details.AbstractMemberHoldingTypeDetailsBuilder;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.support.util.CollectionUtils;
/**
* The required additions to a given type in order to invoke a given application
* layer method, e.g. <code>findAll()</code>. Instances are immutable.
*
* @author Stefan Schmidt
* @author Andrew Swan
* @since 1.2.0
*/
public class MemberTypeAdditions {
/**
* Builds the code snippet for a method call with the given properties
*
* @param targetName the name of the object or class on which the method is
* being invoked (if not blank, must be a valid Java name)
* @param methodName the name of the method being invoked (must be a valid
* Java name)
* @param parameterNames the names of any parameters passed to the method
* @return a non-blank Java snippet
*/
static String buildMethodCall(final String targetName, final String methodName,
final Collection<MethodParameter> parameters) {
JavaSymbolName.assertJavaNameLegal(methodName);
final StringBuilder methodCall = new StringBuilder();
if (StringUtils.isNotBlank(targetName)) {
JavaSymbolName.assertJavaNameLegal(targetName);
methodCall.append(targetName);
methodCall.append(".");
}
methodCall.append(methodName);
methodCall.append("(");
for (final Iterator<MethodParameter> iter = parameters.iterator(); iter.hasNext();) {
final MethodParameter parameter = iter.next();
methodCall.append(parameter.getValue());
if (iter.hasNext()) {
methodCall.append(", ");
}
}
methodCall.append(")");
return methodCall.toString();
}
/**
* Factory method that builds the method call from the given target, method,
* and list of parameter names.
*
* @param builder stores any changes the caller should make in order to make
* the given method call, e.g. the field that is the method
* target (required)
* @param targetName the name of the object or class on which the method is
* being invoked (if not blank, must be a valid Java name)
* @param methodName the name of the method being invoked (must be a valid
* Java name)
* @param isStatic whether the invoked method is static
* @param parameterNames the names of any parameters passed to the method
* (required)
*/
public static MemberTypeAdditions getInstance(final ClassOrInterfaceTypeDetailsBuilder builder,
final String targetName, final String methodName, final boolean isStatic,
final List<MethodParameter> parameters) {
return new MemberTypeAdditions(builder, methodName, buildMethodCall(targetName, methodName,
parameters), isStatic, parameters);
}
/**
* Factory method that builds the method call from the given target, method,
* and array of parameter names.
*
* @param builder stores any changes the caller should make in order to make
* the given method call, e.g. the field that is the method
* target (required)
* @param targetName the name of the object or class on which the method is
* being invoked (if not blank, must be a valid Java name)
* @param methodName the name of the method being invoked (must be a valid
* Java name)
* @param isStatic whether the invoked method is static
* @param parameterNames the names of any parameters passed to the method
* (required)
*/
public static MemberTypeAdditions getInstance(final ClassOrInterfaceTypeDetailsBuilder builder,
final String targetName, final String methodName, final boolean isStatic,
final MethodParameter... parameters) {
return getInstance(builder, targetName, methodName, isStatic, Arrays.asList(parameters));
}
private final ClassOrInterfaceTypeDetailsBuilder classOrInterfaceDetailsBuilder;
private final boolean isStatic;
private final String methodCall;
private final String methodName;
private final List<MethodParameter> methodParameters;
/**
* Constructor that takes a pre-built method call.
*
* @param builder stores any changes the caller should make in order to make
* the given method call, e.g. the field that is the method
* target; can be <code>null</code> if the caller requires no
* changes other than the given method call
* @param methodName the bare name of the method being invoked (required)
* @param methodCall a valid Java snippet that calls the method, including
* any required target and parameters, for example "foo.bar(baz)"
* (required)
* @param isStatic whether the invoked method is static
* @param methodParameters the parameters taken by the invoked method (can
* be <code>null</code>)
*/
public MemberTypeAdditions(final ClassOrInterfaceTypeDetailsBuilder builder,
final String methodName, final String methodCall, final boolean isStatic,
final List<MethodParameter> methodParameters) {
Validate.notBlank(methodName, "Invalid method name '%s'", methodName);
Validate.notBlank(methodCall, "Invalid method signature '%s'", methodCall);
classOrInterfaceDetailsBuilder = builder;
this.methodCall = methodCall;
this.methodName = methodName;
this.methodParameters = new ArrayList<MethodParameter>();
CollectionUtils.populate(this.methodParameters, methodParameters);
this.isStatic = isStatic;
}
/**
* Copies this instance's additions (if any) into the given builder
*
* @param targetBuilder the ITD builder to receive the additions (required)
* @param governorClassOrInterfaceTypeDetails the
* {@link ClassOrInterfaceTypeDetails} of the governor (required)
*/
public void copyAdditionsTo(final AbstractMemberHoldingTypeDetailsBuilder<?> targetBuilder,
final ClassOrInterfaceTypeDetails governorClassOrInterfaceTypeDetails) {
if (classOrInterfaceDetailsBuilder != null) {
classOrInterfaceDetailsBuilder.copyTo(targetBuilder, governorClassOrInterfaceTypeDetails);
}
}
/**
* Returns the field on which this method is invoked
*
* @return <code>null</code> if it's a static method call
* @throws IllegalStateException if there's more than one field in the
* builder
*/
public FieldMetadata getInvokedField() {
if (classOrInterfaceDetailsBuilder == null) {
return null;
}
final List<FieldMetadataBuilder> declaredFields =
classOrInterfaceDetailsBuilder.getDeclaredFields();
switch (declaredFields.size()) {
case 0:
return null;
case 1:
return declaredFields.get(0).build();
default:
throw new IllegalStateException("Multiple fields introduced for " + this);
}
}
/**
* Returns the snippet of Java code that calls the method in question, for
* example "<code>personService.findAll()</code>".
*
* @return a non-blank String
*/
public String getMethodCall() {
return methodCall;
}
/**
* Returns the bare name of the invoked method
*
* @return a non-blank name
*/
public String getMethodName() {
return methodName;
}
/**
* Returns the parameters taken by the invoked method
*
* @return a non-<code>null</code> copy of this list
*/
public List<MethodParameter> getMethodParameters() {
return new ArrayList<MethodParameter>(methodParameters);
}
/**
* Indicates whether this is a static method call
*
* @return see above
*/
public boolean isStatic() {
return isStatic;
}
@Override
public String toString() {
final ToStringBuilder builder = new ToStringBuilder(this);
builder.append("classOrInterfaceDetailsBuilder", classOrInterfaceDetailsBuilder);
builder.append("methodName", methodName);
builder.append("methodCall", methodCall);
return builder.toString();
}
}