package restx.annotations.processor; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Optional; import restx.common.AggregateType; import restx.endpoint.EndpointParameterKind; import java.util.HashMap; import java.util.Map; /** * @author fcamblor * Will build endpoint parameter generated expression during annotation processor * Important note : calls ordering is not important, the only important thing is to call * build() call */ public class ParameterExpressionBuilder { String parameterExpr; String kind; private ParameterExpressionBuilder(String initialExpr, String kind) { this.parameterExpr = initialExpr; this.kind = kind; } private static final Map<AggregateType, Function<String, String>> EMPTY_AGGREGATE_FUNCTIONS = new HashMap<AggregateType, Function<String, String>>(){{ // These (Function) casts are ugly, I know, but read https://github.com/google/guava/issues/1927 put(AggregateType.ITERABLE, (Function) Functions.constant("EMPTY_ITERABLE_SUPPLIER")); put(AggregateType.LIST, (Function) Functions.constant("EMPTY_LIST_SUPPLIER")); put(AggregateType.SET, (Function) Functions.constant("EMPTY_SET_SUPPLIER")); put(AggregateType.COLLECTION, (Function) Functions.constant("EMPTY_COLLECTION_SUPPLIER")); put(AggregateType.ARRAY, new Function<String, String>() { @Override public String apply(String fqcn) { return "Suppliers.ofInstance(new "+fqcn+"{})"; } }); }}; public ParameterExpressionBuilder surroundWithCheckValid( RestxAnnotationProcessor.ResourceMethodParameter parameter) { boolean isOptionalType = parameter.guavaOptional || parameter.java8Optional; // If we don't have any optional type, we should check for non nullity *before* calling checkValid() if(!isOptionalType) { // In case we're on an aggregate interface, parameterExpr will always return a non-null value // (see createFromMapQueryObjectFromRequest() method) so we don't need to add checkNotNull() check on this if(AggregateType.isAggregate(parameter.type)) { } else { // If not an iterable type, ensuring target value is set this.parameterExpr = String.format( "checkNotNull(%s, \"%s param <%s> is required\")", this.parameterExpr, this.kind, parameter.name ); } } Optional<String> validationGroupsExpr = parameter.joinedValidationGroupFQNExpression(); this.parameterExpr = String.format( "checkValid(validator, %s%s)", this.parameterExpr, validationGroupsExpr.isPresent()?","+validationGroupsExpr.get():"" ); if(parameter.guavaOptional) { this.parameterExpr = String.format( "Optional.fromNullable(%s)", this.parameterExpr ); } else if(parameter.java8Optional) { this.parameterExpr = String.format( "java.util.Optional.ofNullable(%s)", this.parameterExpr ); } return this; } public String getParameterExpr() { return parameterExpr; } public static ParameterExpressionBuilder createFromExpr(String expr, String kind) { return new ParameterExpressionBuilder(expr, kind); } public static ParameterExpressionBuilder createFromMapQueryObjectFromRequest( RestxAnnotationProcessor.ResourceMethodParameter parameter, EndpointParameterKind kind){ // We should check if target type is an iterable interface : in that case, we should // instantiate an empty iterable instead of null value if data is missing in request Optional<AggregateType> aggregateType = AggregateType.fromType(parameter.type); String emptySupplierParam = ""; if(aggregateType.isPresent()) { Function<String, String> fqcnToEmptyAggregateTransformer = EMPTY_AGGREGATE_FUNCTIONS.get(aggregateType.get()); if(fqcnToEmptyAggregateTransformer == null) { throw new IllegalStateException("Missing EMPTY_AGGREGATE_FUNCTIONS entry for aggregate type "+aggregateType.get().name()); } emptySupplierParam = ", "+fqcnToEmptyAggregateTransformer.apply(parameter.type); } return new ParameterExpressionBuilder(String.format( "%smapQueryObjectFromRequest(%s.class, \"%s\", request, match, EndpointParameterKind.%s%s)", TypeHelper.isParameterizedType(parameter.type)?"("+parameter.type+")":"", TypeHelper.rawTypeFrom(parameter.type), parameter.reqParamName, kind.name(), emptySupplierParam ), kind.name()); } }