/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.utils.java.model.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.asakusafw.utils.java.model.syntax.ArrayInitializer;
import com.asakusafw.utils.java.model.syntax.ArrayType;
import com.asakusafw.utils.java.model.syntax.ClassBody;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.ModelKind;
import com.asakusafw.utils.java.model.syntax.Name;
import com.asakusafw.utils.java.model.syntax.NamedType;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.Type;
/**
* A builder for building types or their related elements.
*/
public class TypeBuilder {
private final ModelFactory f;
private Type context;
/**
* Creates a new instance.
* @param factory the Java DOM factory
* @param context the context type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public TypeBuilder(ModelFactory factory, Type context) {
if (factory == null) {
throw new IllegalArgumentException("factory must not be null"); //$NON-NLS-1$
}
if (context == null) {
throw new IllegalArgumentException("context must not be null"); //$NON-NLS-1$
}
this.f = factory;
this.context = context;
}
/**
* Returns a copy of this builder.
* @return the copy
*/
public TypeBuilder copy() {
return new TypeBuilder(f, context);
}
/**
* Returns the built type.
* @return the built type
*/
public Type toType() {
return context;
}
/**
* Returns the built type as a named type.
* @return the built type
* @throws IllegalStateException the building type is not a named type
*/
public NamedType toNamedType() {
if (context.getModelKind() != ModelKind.NAMED_TYPE) {
throw new IllegalStateException("context type must be a named type"); //$NON-NLS-1$
}
return (NamedType) context;
}
/**
* Returns the built type as an array type.
* @return the built type
* @throws IllegalStateException the building type is not an array type
*/
public ArrayType toArrayType() {
if (context.getModelKind() != ModelKind.ARRAY_TYPE) {
throw new IllegalStateException("context type must be an array type"); //$NON-NLS-1$
}
return (ArrayType) context;
}
/**
* Returns the parameterized type (as diamond operator).
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder parameterize() {
return parameterize(Collections.emptyList());
}
/**
* Returns the parameterized type.
* @param typeArguments the type arguments
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder parameterize(Type... typeArguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
return parameterize(Arrays.asList(typeArguments));
}
/**
* Returns the parameterized type.
* @param typeArguments the type arguments
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder parameterize(List<? extends Type> typeArguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
return chain(f.newParameterizedType(context, typeArguments));
}
/**
* Returns the parameterized type.
* @param typeArguments the type arguments
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder parameterize(java.lang.reflect.Type... typeArguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
List<Type> args = new ArrayList<>();
for (java.lang.reflect.Type type : typeArguments) {
args.add(Models.toType(f, type));
}
return parameterize(args);
}
/**
* Returns the qualified type.
* @param name the enclosing type name
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder enclose(Name name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (context.getModelKind() == ModelKind.NAMED_TYPE) {
Name enclosed = Models.append(f, toNamedType().getName(), name);
return chain(f.newNamedType(enclosed));
} else {
Type current = context;
for (SimpleName segment : Models.toList(name)) {
current = f.newQualifiedType(current, segment);
}
return chain(current);
}
}
/**
* Returns the qualified type.
* @param name the enclosing type name
* @return this
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public TypeBuilder enclose(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return enclose(Models.toName(f, name));
}
/**
* Returns the array type.
* @param dimensions the number of dimensions
* @return this
* @throws IllegalArgumentException if the parameter is a negative value
*/
public TypeBuilder array(int dimensions) {
if (dimensions < 0) {
throw new IllegalArgumentException("dimensions must be greater than or equal to 0"); //$NON-NLS-1$
}
Type current = context;
for (int i = 0; i < dimensions; i++) {
current = f.newArrayType(current);
}
return chain(current);
}
/**
* Returns a class literal.
* @return the chained expression builder
*/
public ExpressionBuilder dotClass() {
return expr(f.newClassLiteral(context));
}
/**
* Returns a qualified this.
* @return the chained expression builder
*/
public ExpressionBuilder dotThis() {
return expr(f.newThis(toNamedType()));
}
/**
* Returns the array instance creation expression.
* @param dimensions the number of array elements for each dimension
* @return the chained expression builder
* @throws IllegalStateException if the building type is not an array type
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newArray(int... dimensions) {
if (dimensions == null) {
throw new IllegalArgumentException("dimensions must not be null"); //$NON-NLS-1$
}
List<Expression> exprs = new ArrayList<>();
for (int dim : dimensions) {
exprs.add(Models.toLiteral(f, dim));
}
return newArray(exprs);
}
/**
* Returns the array instance creation expression.
* @param dimensions the number of array elements for each dimension
* @return the chained expression builder
* @throws IllegalStateException if the building type is not an array type
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newArray(Expression... dimensions) {
if (dimensions == null) {
throw new IllegalArgumentException("dimensions must not be null"); //$NON-NLS-1$
}
return newArray(Arrays.asList(dimensions));
}
/**
* Returns the array instance creation expression.
* @param dimensions the number of array elements for each dimension
* @return the chained expression builder
* @throws IllegalStateException if the building type is not an array type
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newArray(List<? extends Expression> dimensions) {
if (dimensions == null) {
throw new IllegalArgumentException("dimensions must not be null"); //$NON-NLS-1$
}
return expr(f.newArrayCreationExpression(toArrayType(), dimensions, null));
}
/**
* Returns the array instance creation expression.
* @param initializer the array initializer
* @return the chained expression builder
* @throws IllegalStateException if the building type is not an array type
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newArray(ArrayInitializer initializer) {
if (initializer == null) {
throw new IllegalArgumentException("initializer must not be null"); //$NON-NLS-1$
}
return expr(f.newArrayCreationExpression(toArrayType(), Collections.emptyList(), initializer));
}
/**
* Returns the class instance creation expression.
* @param arguments the constructor arguments
* @return the chained expression builder
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newObject(Expression... arguments) {
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return newObject(Arrays.asList(arguments), null);
}
/**
* Returns the class instance creation expression.
* @param arguments the constructor arguments
* @return the chained expression builder
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newObject(List<? extends Expression> arguments) {
return newObject(arguments, null);
}
/**
* Returns the class instance creation expression.
* @param arguments the constructor arguments
* @param anonymousClassBlock the anonymous class block (nullable)
* @return the chained expression builder
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder newObject(List<? extends Expression> arguments, ClassBody anonymousClassBlock) {
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return expr(f.newClassInstanceCreationExpression(
null,
Collections.emptyList(),
context,
arguments,
anonymousClassBlock));
}
/**
* Returns the field access expression.
* @param name the target field name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder field(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return field(f.newSimpleName(name));
}
/**
* Returns the field access expression.
* @param name the target field name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ExpressionBuilder field(SimpleName name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return expr(f.newQualifiedName(toNamedType().getName(), name));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(String name, Expression... arguments) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(Collections.emptyList(), name, Arrays.asList(arguments));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(List<? extends Type> typeArguments, String name, Expression... arguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(typeArguments, name, Arrays.asList(arguments));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(String name, List<? extends Expression> arguments) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(Collections.emptyList(), name, arguments);
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(
List<? extends Type> typeArguments,
String name,
List<? extends Expression> arguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(typeArguments, f.newSimpleName(name), arguments);
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(SimpleName name, Expression... arguments) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(Collections.emptyList(), name, Arrays.asList(arguments));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(List<? extends Type> typeArguments, SimpleName name, Expression... arguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(typeArguments, name, Arrays.asList(arguments));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(SimpleName name, List<? extends Expression> arguments) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return method(Collections.emptyList(), name, arguments);
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @param arguments the method arguments
* @return the chained expression builder
* @throws IllegalStateException the building type is not a named type
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder method(
List<? extends Type> typeArguments,
SimpleName name,
List<? extends Expression> arguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (arguments == null) {
throw new IllegalArgumentException("arguments must not be null"); //$NON-NLS-1$
}
return expr(f.newMethodInvocationExpression(toNamedType().getName(), typeArguments, name, arguments));
}
/**
* Returns the constructor invocation using the building type as its qualifier.
* @return the chained expression builder
*/
public ExpressionBuilder constructorReference() {
return constructorReference(Collections.emptyList());
}
/**
* Returns the constructor invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @return the chained expression builder
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder constructorReference(List<? extends Type> typeArguments) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
return expr(f.newConstructorReferenceExpression(context, typeArguments));
}
/**
* Returns the method reference using the building type as its qualifier.
* @param name the target method name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder methodReference(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return methodReference(Collections.emptyList(), f.newSimpleName(name));
}
/**
* Returns the method reference using the building type as its qualifier.
* @param name the target method name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder methodReference(SimpleName name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return methodReference(Collections.emptyList(), name);
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder methodReference(List<? extends Type> typeArguments, String name) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return methodReference(typeArguments, f.newSimpleName(name));
}
/**
* Returns the method invocation using the building type as its qualifier.
* @param typeArguments the type arguments
* @param name the target method name
* @return the chained expression builder
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public ExpressionBuilder methodReference(List<? extends Type> typeArguments, SimpleName name) {
if (typeArguments == null) {
throw new IllegalArgumentException("typeArguments must not be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
return expr(f.newMethodReferenceExpression(context, typeArguments, name));
}
private TypeBuilder chain(Type type) {
assert type != null;
this.context = type;
return this;
}
private ExpressionBuilder expr(Expression expression) {
assert expression != null;
return new ExpressionBuilder(f, expression);
}
}