com.google.cloud.dataflow.sdk.util.InstanceBuilder Maven / Gradle / Ivy
Show all versions of google-cloud-dataflow-java-sdk-all Show documentation
/*
* Copyright (C) 2015 Google Inc.
*
* 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.google.cloud.dataflow.sdk.util;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import com.google.cloud.dataflow.sdk.values.TypeDescriptor;
import com.google.common.base.Joiner;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nullable;
/**
* Utility for creating objects dynamically.
*
* @param type type of object returned by this instance builder
*/
public class InstanceBuilder {
/**
* Create an InstanceBuilder for the given type.
*
* The specified type is the type returned by {@link #build}, which is
* typically the common base type or interface of the instance being
* constructed.
*/
public static InstanceBuilder ofType(Class type) {
return new InstanceBuilder<>(type);
}
/**
* Create an InstanceBuilder for the given type.
*
* The specified type is the type returned by {@link #build}, which is
* typically the common base type or interface for the instance to be
* constructed.
*
*
The TypeDescriptor argument allows specification of generic types. For example,
* a {@code List} return type can be specified as
* {@code ofType(new TypeDescriptor>(){})}.
*/
public static InstanceBuilder ofType(TypeDescriptor token) {
@SuppressWarnings("unchecked")
Class type = (Class) token.getRawType();
return new InstanceBuilder<>(type);
}
/**
* Sets the class name to be constructed.
*
* If the name is a simple name (ie {@link Class#getSimpleName()}), then
* the package of the return type is added as a prefix.
*
*
The default class is the return type, specified in {@link #ofType}.
*
*
Modifies and returns the {@code InstanceBuilder} for chaining.
*
* @throws ClassNotFoundException if no class can be found by the given name
*/
public InstanceBuilder fromClassName(String name)
throws ClassNotFoundException {
checkArgument(factoryClass == null, "Class name may only be specified once");
if (name.indexOf('.') == -1) {
name = type.getPackage().getName() + "." + name;
}
try {
factoryClass = Class.forName(name);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException(
String.format("Could not find class: %s", name), e);
}
return this;
}
/**
* Sets the factory class to use for instance construction.
*
* Modifies and returns the {@code InstanceBuilder} for chaining.
*/
public InstanceBuilder fromClass(Class factoryClass) {
this.factoryClass = factoryClass;
return this;
}
/**
* Sets the name of the factory method used to construct the instance.
*
* The default, if no factory method was specified, is to look for a class
* constructor.
*
*
Modifies and returns the {@code InstanceBuilder} for chaining.
*/
public InstanceBuilder fromFactoryMethod(String methodName) {
checkArgument(this.methodName == null,
"Factory method name may only be specified once");
this.methodName = methodName;
return this;
}
/**
* Adds an argument to be passed to the factory method.
*
* The argument type is used to lookup the factory method. This type may be
* a supertype of the argument value's class.
*
*
Modifies and returns the {@code InstanceBuilder} for chaining.
*
* @param the argument type
*/
public InstanceBuilder withArg(Class argType, ArgT value) {
parameterTypes.add(argType);
arguments.add(value);
return this;
}
/**
* Creates the instance by calling the factory method with the given
* arguments.
*
* Defaults
*
* - factory class: defaults to the output type class, overridden
* via {@link #fromClassName(String)}.
*
- factory method: defaults to using a constructor on the factory
* class, overridden via {@link #fromFactoryMethod(String)}.
*
*
* @throws RuntimeException if the method does not exist, on type mismatch,
* or if the method cannot be made accessible.
*/
public T build() {
if (factoryClass == null) {
factoryClass = type;
}
Class[] types = parameterTypes
.toArray(new Class[parameterTypes.size()]);
// TODO: cache results, to speed repeated type lookups?
if (methodName != null) {
return buildFromMethod(types);
} else {
return buildFromConstructor(types);
}
}
/////////////////////////////////////////////////////////////////////////////
/**
* Type of object to construct.
*/
private final Class type;
/**
* Types of parameters for Method lookup.
*
* @see Class#getDeclaredMethod(String, Class[])
*/
private final List> parameterTypes = new LinkedList<>();
/**
* Arguments to factory method {@link Method#invoke(Object, Object...)}.
*/
private final List