/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jvnet.jaxb2_commons.plugin.fluent_api;
import java.util.Collection;
import java.util.List;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JConditional;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JForEach;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
/**
* @author Hanson Char
*/
public enum FluentMethodType {
FLUENT_SETTER
{
/**
* Adds a fluent api method, which invokes the given setter method, to the given class.
* This applies to both simple property setter method and
* indexed property setter method.
*/
@Override
public void createFluentMethod(JDefinedClass implClass, FluentMethodInfo fluentMethodInfo)
{
JMethod setterMethod = fluentMethodInfo.getJmethod();
String name = setterMethod.name();
// Create a with* method for the respective set* method.
int mods = JMod.PUBLIC | setterMethod.mods().getValue() & JMod.FINAL;
JMethod fluentMethod = implClass.method(mods, implClass,
FLUENT_SETTER_METHOD_PREFIX + name.substring(SETTER_METHOD_PREFIX_LEN));
if (fluentMethodInfo.isOverride())
fluentMethod.annotate(Override.class);
JVar[] jvars = setterMethod.listParams();
// jvars.length == 1 means simple property setter method.
// jvars.length == 2 means indexed property setter method.
assert jvars.length == 1 || jvars.length == 2;
// with the same parameter(s) as the set* method
for (JVar jvar : jvars)
fluentMethod.param(jvar.mods().getValue(), jvar.type(), jvar.name());
JBlock jblock = fluentMethod.body();
// The with* method in turn invoke the setter method
JInvocation jinvocation = jblock.invoke(setterMethod);
// passing the same list of arguments
for (JVar jvar : jvars)
jinvocation.arg(jvar);
// and return "this"
jblock._return(JExpr._this());
return;
}
},
FLUENT_LIST_SETTER
{
/**
* Create a fluent setter method for List, with support of variable arguments.
*/
@Override
public void createFluentMethod(JDefinedClass implClass, FluentMethodInfo fluentMethodInfo)
{
JMethod listGetterMethod = fluentMethodInfo.getJmethod();
String name = listGetterMethod.name();
// Create a with* method for the respective List<T> get* method.
int mods = JMod.PUBLIC | listGetterMethod.mods().getValue() & JMod.FINAL;
JMethod fluentMethod = implClass.method(mods, implClass,
FLUENT_SETTER_METHOD_PREFIX + name.substring(GETTER_METHOD_PREFIX_LEN));
if (fluentMethodInfo.isOverride())
fluentMethod.annotate(Override.class);
JType returnJType = listGetterMethod.type();
// As is already checked in isListGetterMethod(JMethod):
// 1) the return type must be a subtype of JClass; and
// 2) the number of type parameters must be 1
JClass returnJClass = JClass.class.cast(returnJType);
List<JClass> typeParams = returnJClass.getTypeParameters();
assert typeParams.size() == 1;
JClass typeParam = typeParams.get(0);
// Support variable arguments
JVar jvarParam = fluentMethod.varParam(typeParam, VALUES);
JBlock body = fluentMethod.body();
JConditional cond = body._if(
jvarParam.ne(
JExpr._null()));
JForEach forEach = cond._then()
.forEach(
typeParam, VALUE, JExpr.ref(VALUES));
JInvocation addInvocation = forEach.body()
.invoke(
JExpr.invoke(listGetterMethod), "add");
addInvocation.arg(
JExpr.ref(VALUE));
// and return "this"
body._return(
JExpr._this());
return;
}
},
FLUENT_COLLECTION_SETTER
{
// Originally proposed by Alex Wei ozgwei@dev.java.net:
// https://jaxb2-commons.dev.java.net/issues/show_bug.cgi?id=12
/**
* Create a fluent setter method for List, with support of a java.util.Collection argument.
*/
@Override
public void createFluentMethod(JDefinedClass implClass, FluentMethodInfo fluentMethodInfo)
{
JMethod listGetterMethod = fluentMethodInfo.getJmethod();
String name = listGetterMethod.name();
// Create a with* method for the respective List<T> get* method.
int mods = JMod.PUBLIC | listGetterMethod.mods().getValue() & JMod.FINAL;
JMethod fluentMethod = implClass.method(mods, implClass,
FLUENT_SETTER_METHOD_PREFIX + name.substring(GETTER_METHOD_PREFIX_LEN));
if (fluentMethodInfo.isOverride())
fluentMethod.annotate(Override.class);
JType returnJType = listGetterMethod.type();
// As is already checked in isListGetterMethod(JMethod):
// 1) the return type must be a subtype of JClass; and
// 2) the number of type parameters must be 1
JClass returnJClass = JClass.class.cast(returnJType);
List<JClass> typeParams = returnJClass.getTypeParameters();
assert typeParams.size() == 1;
JClass typeParam = typeParams.get(0);
// Support Collection with type parameter
JClass narrowedCollectionJClass = implClass.owner().ref(Collection.class).narrow(typeParam);
JVar jvarParam = fluentMethod.param(narrowedCollectionJClass, VALUES);
JBlock body = fluentMethod.body();
JConditional cond = body._if(
jvarParam.ne(
JExpr._null()));
JInvocation addInvocation = cond._then()
.invoke(
JExpr.invoke(listGetterMethod), "addAll");
addInvocation.arg(jvarParam);
// and return "this"
body._return(
JExpr._this());
return;
}
}
;
private static final String VALUE = "value";
private static final String VALUES = "values";
public static final String GETTER_METHOD_PREFIX = "get";
public static final String SETTER_METHOD_PREFIX = "set";
public static final String FLUENT_SETTER_METHOD_PREFIX = "with";
public static final String PARAMETERIZED_LIST_PREFIX = List.class.getName() + "<";
public static final int SETTER_METHOD_PREFIX_LEN = SETTER_METHOD_PREFIX.length();
public static final int GETTER_METHOD_PREFIX_LEN = GETTER_METHOD_PREFIX.length();
public abstract void createFluentMethod(JDefinedClass implClass, FluentMethodInfo fluentMethodInfo);
}