/*****************************************************************************
* Copyright (C) Codehaus.org. All rights reserved. *
* ------------------------------------------------------------------------- *
* The software in this package is published under the terms of the BSD *
* style license a copy of which has been included with this distribution in *
* the LICENSE.txt file. *
*****************************************************************************/
/*
* Created on Feb 28, 2005
*
* Author Ben Yu
* ZBS
*/
package jfun.yan;
import java.beans.IntrospectionException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import jfun.util.Misc;
import jfun.util.StringUtils;
import jfun.util.beans.Bean;
import jfun.util.beans.BeanType;
import jfun.util.beans.NoSuchPropertyException;
import jfun.yan.element.ElementStore;
import jfun.yan.element.ListStore;
import jfun.yan.element.MapStore;
import jfun.yan.element.SetStore;
import jfun.yan.factory.Factory;
import jfun.yan.factory.Pool;
import jfun.yan.function.Function;
import jfun.yan.function.Property2Signature;
import jfun.yan.function.Signature;
import jfun.yan.util.Predicate;
import jfun.yan.util.ReflectionUtil;
/**
* This class provides implementations of some basic components
* as well as most of the commonly used combiniators
* for Component.
* A combinator is a function that will combine one or more Component objects
* and create a more complex Component object.
*
* <p>
* Codehaus.org.
*
* @author Ben Yu
*
*/
public final class Components {
/**
* To adapt a Creator object to a Component object.
* if the object is already a Component, itself is returned.
* @param c the creator object.
* @return the Component object.
*/
public static <T> Component<T> adapt(Creator<T> c){
if(c instanceof Component){
return (Component<T>)c;
}
else return new Creator2Component<T>(c);
}
/**
* Create a Component object that uses a Function to create instance.
* @param fun the Function object.
* @return the Component object.
*/
public static <T> Component<T> fun(Function<T> fun){
//do not label it because this extra frame does not bring
//much useful information.
return new FunctionComponent<T>(fun).guard();
}
/**
* Create a Component object that uses the constructor of a class
* to create instance of that class.
* This class should have one and only one public constructor.
* <br>
* Array type is given a special constructor so that the constructor of
* array type T has one parameter of type int
* and calls new T[size] when invoked.
* @param c the class.
* @return the Component object.
* @throws IllegalArgumentException
* if the class is not public or has no public constructor
* or has more than one public constructor.
*/
public static <T> Component<T> ctor(Class<T> c)
throws IllegalArgumentException{
return fun(Functions.ctor(c));
}
/**
* Create a Component that uses one constructor of a class
* to create instance of that class.
* The constructor to invoke is determined by the parameter types specified.
* <br>
* Array type is given a special constructor so that the constructor of
* array type T has one parameter of type int
* and calls new T[size] when invoked.
* @param c the class.
* @param param_types the parameter types of the constructor to use.
* null indicates the default constructor.
* @return the Component object.
* @throws IllegalArgumentException
* if the type is not public or the public constructor cannot be found.
*/
public static <T> Component<T> ctor(Class<T> c, Class[] param_types)
throws IllegalArgumentException{
return fun(Functions.ctor(c, param_types));
}
/**
* Create a Component object that invokes a static method to create instance.
* This class should have one and only one public static method of this name.
* @param c the class where the static method belongs.
* @param name the static method name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. this method is not static. <br>
* 3. more than one public static method with the specified name. <br>
* 4. no public static method with the specified name. <br>
*/
public static Component static_method(Class c, String name)
throws IllegalArgumentException{
return fun(Functions.static_method(c, name));
}
/**
* Create a Component object that invokes a static method to create instance.
* The static method to invoke is determined by the method name
* and the parameter types.
* @param c the class where the static method belongs.
* @param name the static method name.
* @param param_types the parameter types.
* null indicates a parameter-less method.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. this method is not static. <br>
* 3. no public static method with the specified name and the parameter types. <br>
*/
public static Component static_method(
Class c, String name, Class[] param_types){
return fun(
Functions.static_method(c, name, param_types));
}
/**
* Create a Component object that invokes a method against a given object
* to create component instance.
* The class of the object
* should have one and only one public method of this name.
* @param obj the object to run the method against. It cannot be null.
* @param name the method name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. more than one public method with the specified name. <br>
* 3. no public method with the specified name. <br>
*/
public static Component method(Object obj, String name){
return fun(Functions.method(obj, name));
}
/**
* Create a Component object that invokes a method against a given object
* to create component instance.
* The class of the object
* should have one and only one public static method of this name.
* @param type the Class object to look up method.
* This parameter can be used when we only want to look up in
* a super type rather than obj.getClass().
* @param obj the object to run the method against.
* It can be null if the method is static.
* @param name the method name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. more than one public method with the specified name. <br>
* 3. no public method with the specified name. <br>
*/
public static Component method(Class type, Object obj, String name){
return fun(Functions.method(type, obj, name));
}
/**
* Create a Component object that invokes a method against a given object
* to create component instance.
* The method to invoke is determined by the method name
* and the parameter types.
* @param obj the object to run the method against.
* It cannot be null.
* @param name the method name.
* @param param_types the parameter types.
* null indicates a parameter-less method.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. no public method with the specified name and the parameter types. <br>
*/
public static Component method(
Object obj, String name, Class[] param_types){
return fun(
Functions.method(obj, name, param_types));
}
/**
* Create a Component object that invokes a method against a given object
* to create component instance.
* The method to invoke is determined by the method name
* and the parameter types.
* @param type the Class object to look up method.
* This parameter can be used when we only want to look up in
* a super type rather than obj.getClass().
* @param obj the object to run the method against.
* It can be null if the method is static.
* @param name the method name.
* @param param_types the parameter types.
* null indicates a parameter-less method.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. this method is not public. <br>
* 2. no public method with the specified name and the parameter types. <br>
*/
public static Component method(Class type,
Object obj, String name, Class[] param_types){
return fun(
Functions.method(type, obj, name, param_types));
}
/**
* Create a Component object that reads a static field to create instance.
* @param c the class where the static instance belongs.
* @param name the static instance name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. field not found. <br>
* 2. field is not static. <br>
*/
public static Component static_field(Class c, String name)
throws IllegalArgumentException{
return fun(Functions.static_field(c, name));
}
/**
* Create a Component object that reads a field from a given object
* to create component instance.
* @param type the Class object to look up field.
* This parameter can be used when we only want to look up in
* a super type rather than obj.getClass().
* @param obj the object to read field from.
* It can be null if the field is static.
* @param name the field name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. field is not found.
*/
public static Component field(Class type, Object obj, String name){
return fun(Functions.field(type, obj, name));
}
/**
* Create a Component object that reads a field from a given object
* to create component instance.
* @param obj the object to read the field from. It cannot be null.
* @param name the field name.
* @return the Component object.
* @throws IllegalArgumentException
* thrown if any of the following conditions is true: <br>
* 1. field not found.
*/
public static Component field(Object obj, String name){
return fun(Functions.field(obj, name));
}
/**
* Create a Component that calls a property getter of an object.
* @param type the type to introspect.
* @param obj the object to run the getter against.
* @param prop the property name.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the property does not exist.
* @throws NonReadablePropertyException if the property is not rreadable.
*/
public static Component getter(Class type, Object obj, String prop)
throws IntrospectionException, NoSuchPropertyException,
NonReadablePropertyException{
final BeanType btype = BeanType.instance(type);
final Method m = btype.getReader(prop);
if(m==null)
throw new NonReadablePropertyException(type, prop);
return fun(Functions.getter(Bean.instance(type, obj), prop));
}
/**
* Create a Component that calls a property setter against an object.
*
* @param type the type to introspect.
* @param obj the object.
* @param prop the property name.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the the property cannot be found.
* @throws NonWritablePropertyException if the property is not writable.
*/
public static <T> Component<T> setter(Class type, T obj, String prop)
throws IntrospectionException, NoSuchPropertyException,
NonWritablePropertyException{
final BeanType btype = BeanType.instance(type);
if(btype.getWriter(prop)==null && btype.getIndexedWriter(prop)==null)
throw new NonWritablePropertyException(type, prop);
return fun(Functions.setter(Bean.instance(type, obj), prop));
}
/**
* Create a Component that calls an indexed property getter of an object.
* @param type the type to introspect.
* @param obj the object to run the getter against.
* @param prop the property name.
* @param ind the index.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the property does not exist.
* @throws NonReadablePropertyException
* if the property is not rreadable or if the property is not indexed.
*/
public static Component indexed_getter(Class type, Object obj, String prop, int ind)
throws IntrospectionException, NoSuchPropertyException,
NonReadablePropertyException{
final BeanType btype = BeanType.instance(type);
final Method m = btype.getIndexedReader(prop);
if(m==null)
throw new NonReadablePropertyException(type, prop);
return fun(Functions.indexed_getter(Bean.instance(type, obj), prop, ind));
}
/**
* Create a Component that calls an indexed property setter against an object.
*
* @param type the type to introspect.
* @param obj the object.
* @param prop the property name.
* @param ind the index.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the the property cannot be found.
* @throws NonWritablePropertyException
* if the property is not writable or not indexed.
*/
public static <T> Component<T> indexed_setter(Class type, T obj, String prop, int ind)
throws IntrospectionException, NoSuchPropertyException,
NonWritablePropertyException{
final BeanType btype = BeanType.instance(type);
final Method m = btype.getIndexedWriter(prop);
if(m==null)
throw new NonWritablePropertyException(type, prop);
return fun(Functions.indexed_setter(Bean.instance(type, obj), prop, ind));
}
/**
* Create a Component that calls a property getter of an object.
* @param obj the object to run the getter against.
* @param prop the property name.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the property does not exist.
* @throws NonReadablePropertyException if the property is not rreadable.
*/
public static Component getter(Object obj, String prop)
throws IntrospectionException, NoSuchPropertyException,
NonReadablePropertyException{
return getter(obj.getClass(), obj, prop);
}
/**
* Create a Component that calls a property setter against an object.
*
* @param obj the object to run the setter against.
* @param prop the property name.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the the property cannot be found.
* @throws NonWritablePropertyException if the property is not writable.
*/
public static <T> Component<T> setter(T obj, String prop)
throws IntrospectionException, NoSuchPropertyException,
NonWritablePropertyException{
return setter(obj.getClass(), obj, prop);
}
/**
* Create a Component that calls an indexed property getter of an object.
* @param obj the object to run the getter against.
* @param prop the property name.
* @param ind the index.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the property does not exist.
* @throws NonReadablePropertyException
* if the property is not rreadable or if the property is not indexed.
*/
public static Component indexed_getter(Object obj, String prop, int ind)
throws IntrospectionException, NoSuchPropertyException,
NonReadablePropertyException{
return indexed_getter(obj.getClass(), obj, prop, ind);
}
/**
* Create a Component that calls an indexed property setter against an object.
*
* @param obj the object to run the setter against.
* @param prop the property name.
* @param ind the index.
* @return the Component object.
* @throws IntrospectionException if introspection fails.
* @throws NoSuchPropertyException if the the property cannot be found.
* @throws NonWritablePropertyException
* if the property is not writable or not indexed.
*/
public static <T> Component<T> indexed_setter(T obj, String prop, int ind)
throws IntrospectionException, NoSuchPropertyException,
NonWritablePropertyException{
return indexed_setter(obj.getClass(), obj, prop, ind);
}
/**
* Create a Component object that simply return a value upon instance creation.
* The type of the value is used as the type of the component.
* null is permitted as a value too.
* void.class is used as the type of the component if the value is null.
* @param val the value.
* @return the Component object.
*/
public static <T> Component<T> value(final T val){
if(val==null) return NilComponent.instance;
return new ValueComponent<T>(val);
}
/**
* Create a Component object that simply return a value upon instance creation.
* double.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Double> value(final double val){
return value(new Double(val)).cast(double.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* float.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Float> value(final float val){
return value(new Float(val)).cast(float.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* long.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Long> value(final long val){
return value(new Long(val)).cast(long.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* int.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Integer> value(final int val){
return value(new Integer(val)).cast(int.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* short.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Short> value(final short val){
return value(new Short(val)).cast(short.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* byte.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Byte> value(final byte val){
return value(new Byte(val)).cast(byte.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* char.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Character> value(final char val){
return value(new Character(val)).cast(char.class);
}
/**
* Create a Component object that simply return a value upon instance creation.
* boolean.class is used as the component type.
* @param val the value.
* @return the Component object.
*/
public static Component<Boolean> value(final boolean val){
return val?true_component:false_component;
}
private static Component<Boolean> return_bool(boolean b){
return value(Boolean.valueOf(b)).cast(boolean.class);
}
private static final Component<Boolean> true_component = return_bool(true);
private static final Component<Boolean> false_component = return_bool(false);
/**
* Create a new Component object from a base Component object
* that calls Java Bean setters after the base Component creates the instance.
* <br>
* Upon instance creation, it first calls the base Component.
* The base Component is responsible for creating the component instance.
* The bean component then calls the java bean setters
* for the property names specified against the component instance
* to finish the component creation process.
* <br>
* The Java Bean setters used to create the component instance are
* determined by the return value of getType() method.
* That means, if getType() returns Type1 while the actual instance created is
* a subtype of Type1, only the setters of Type1 are used.
* However, if the base component is late-bound, where the getType() returns null,
* the Java Bean setters are resolved at creation time.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param cc the base Component object.
* @param props the names of the properties used.
* @return the new Component object.
* @throws IntrospectionException if java bean introspection fails.
* @throws IllegalPropertyNameException if a name specified in
* the props Set is not a valid property name.
* @throws NonWritablePropertyException
* if a property specified has no setter.
*/
public static <T> Component<T> bean(final Component<T> cc, java.util.Set props)
throws IntrospectionException,
IllegalPropertyNameException, NonWritablePropertyException{
final PropertiesInjector pi =
DefaultPropertiesInjector.instance(cc.getType(), props);
return makeBean(cc, pi);
/*
return BeanBinder.instance(cc, props).guard()
.label();
*/
}
/**
* Create a Bean component that calls a PropertiesInjector to inject
* bean properties to the bean instance.
* @param cc the component that instantiates the bean instance.
* @param injector the injector to inject properties.
* @return the bean component.
*/
public static <T> Component<T> makeBean(Component<T> cc, PropertiesInjector injector){
return makeBean(injector.toString(), cc, injector);
}
/**
* Create a Bean component that calls a PropertiesInjector to inject
* bean properties to the bean instance.
* @param name the name of the bean component.
* @param cc the component that instantiates the bean instance.
* @param injector the injector to inject properties.
* @return the bean component.
*/
public static <T> Component<T> makeBean(String name,
Component<T> cc, PropertiesInjector injector){
return Monad.followedBy(cc, new BeanBinder<T>(name, injector)).label(name);
}
/**
* Create a new Component object from a base Component object
* that calls Java Bean setters after the base Component creates the instance.
* <br>
* Upon instance creation, it first calls the base Component.
* The base Component is responsible for creating the component instance.
* The bean component then calls the java bean setters to the component instance
* to finish the component creation process.
* <br>
* The Java Bean setters used to create the component instance are
* determined by the return value of getType() method.
* That means, if getType() returns Type1 while the actual instance created is
* a subtype of Type1, only the setters of Type1 are used.
* However, if the base component is late-bound, where the getType() returns null,
* the Java Bean setters are resolved at creation time.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param cc the base Component object.
* @return the new Component object.
* @throws IntrospectionException if java bean introspection fails.
*/
public static <T> Component<T> bean(final Component<T> cc)
throws IntrospectionException{
final PropertiesInjector pi = DefaultPropertiesInjector.instance(cc.getType());
return makeBean(cc, pi);
//return BeanBinder.instance(cc).guard().label();
}
/**
* Create a Component object that uses Java Bean convention to create instance.
* i.e. It first calls the default constructor of the class, then calls all
* the Java Bean setters to finish the instance creation.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param type the Java Bean class.
* @return the Component object.
* @throws IntrospectionException if Java Bean introspection fails.
* @throws IllegalArgumentException if the class is not public
* or does not have public default constructor.
*/
public static <T> Component<T> bean(final Class<T> type)
throws IntrospectionException, IllegalArgumentException{
return bean(ctor(type, null));
}
/**
* Create a Component object that uses Java Bean convention to create instance.
* i.e. It first calls the default constructor of the class, then calls all
* the Java Bean setters for the properties specified to finish the instance creation.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param type the Java Bean class.
* @param props the names of the properties used.
* @return the new Component object.
* @throws IntrospectionException if Java Bean introspection fails.
* @throws IllegalPropertyNameException if any property name specified is not valid.
* @throws NonWritablePropertyException if any property specified has no setter.
*/
public static <T> Component<T> bean(final Class<T> type, java.util.Set props)
throws IntrospectionException, IllegalPropertyNameException,
NonWritablePropertyException{
return Components.bean(ctor(type, null), props);
}
/**
* Create a new Component object from a base Component object
* that calls Java Bean setters after the base Component creates the instance.
* <br>
* Upon instance creation, it first calls the base Component.
* The base Component is responsible for creating the component instance.
* The bean component then calls the java bean setters
* for the property names specified against the component instance
* to finish the component creation process.
* <br>
* The Java Bean setters used to create the component instance are
* determined by the return value of getType() method.
* That means, if getType() returns Type1 while the actual instance created is
* a subtype of Type1, only the setters of Type1 are used.
* However, if the base component is late-bound, where the getType() returns null,
* the Java Bean setters are resolved at creation time.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param cc the base Component object.
* @param props the names of the properties used.
* @return the new Component object.
* @throws IntrospectionException if java bean introspection fails.
* @throws IllegalPropertyNameException if a name specified in
* the props Set is not a valid property name.
* @throws NonWritablePropertyException
* if a property specified has no setter.
* @throws IllegalArgumentException if the props array has any duplicate name.
*/
public static <T> Component<T> bean(final Component<T> cc, String... props)
throws IntrospectionException, IllegalPropertyNameException,
NonWritablePropertyException, IllegalArgumentException{
return Components.bean(cc, jfun.yan.util.Utils.toSet(props, "property name"));
}
/**
* Create a Component object that uses Java Bean convention to create instance.
* i.e. It first calls the default constructor of the class, then calls all
* the Java Bean setters for the properties specified to finish the instance creation.
* <br>
* It should be noted that,
* for a singleton bean component obtained by calling singleton() method
* against the bean component, it does not cause infinite loop
* if any property of the component is depending on this same singleton bean componet.
* This is because the singleton bean component caches the instance
* before any of the properties are set.
* @param type the Java Bean class.
* @param props the names of the properties used.
* @return the new Component object.
* @throws IntrospectionException if Java Bean introspection fails.
* @throws IllegalPropertyNameException if any property name specified is not valid.
* @throws NonWritablePropertyException if any property specified has no setter.
* @throws IllegalArgumentException if the props array has any duplicate name.
*/
public static <T> Component<T> bean(final Class<T> type, String[] props)
throws IntrospectionException, IllegalPropertyNameException,
NonWritablePropertyException, IllegalArgumentException{
return bean(ctor(type, null), props);
}
/**
* Creates a Component that will call a certain method
* against the instance created by another Component.
* The return value of the method is used as the result of the
* created Component.
* @param cc the Component to create the instance.
* @param mtd the methdo to call against the instance.
* @return the new Component object.
*/
public static Component bindMethod(final Component cc, final Method mtd){
return Monad.bind(autowired(cc), new MethodBinder(mtd)).label(""+cc+".<"+mtd+">")
.guard();
}
/**
* Creates a Component that will call a certain method
* against the instance created by another Component.
* The return value of the method is used as the result of the
* created Component.
* <br>
* If the type of the first component is statically known (getType()!=null),
* The method is statically resolved when bindMethod() is called.
* Otherwise, it is resolved when the actual component instantiation happens.
* @param cc the Component to create the instance.
* @param name the method name.
* @param suppress_security whether access to non-public method is allowed.
* @return the new Component object.
* @throws IllegalArgumentException if the static resolution of the method
* fails.
*/
public static Component bindMethod(final Component cc, final String name,
boolean suppress_security)
throws IllegalArgumentException{
final String lbl = ""+cc+"."+name + "( ... )";
final Class type = cc.getType();
return Monad.bind(autowired(cc), invokingMethod(type, name, suppress_security))
.label(lbl).guard();
}
/**
* Creates a Component that will call a certain method
* against the instance created by another Component.
* The return value of the method is used as the result of the
* created Component.
* <br>
* If the type of the first component is statically known (getType()!=null),
* The method is statically resolved when bindMethod() is called.
* Otherwise, it is resolved when the actual component instantiation happens.
* @param cc the Component to create the instance.
* @param name the method name.
* @param param_count the number of formal parameters.
* @param suppress_security whether access to non-public method is allowed.
* @return the new Component object.
* @throws IllegalArgumentException if the static resolution of the method
* fails.
*/
public static Component bindMethod(final Component cc,
final String name, final int param_count,
boolean suppress_security)
throws IllegalArgumentException{
final String lbl = ReflectionUtil.toMethodString(name, param_count);
final Class type = cc.getType();
return Monad.bind(autowired(cc), invokingMethod(type, name, param_count, suppress_security))
.label(lbl).guard();
}
/**
* Creates a Component that will read a certain field
* from the instance created by another Component.
* The return value of the field is used as the result of the
* created Component.
* @param cc the Component to create the instance.
* @param fld the methdo to read from the instance.
* @return the new Component object.
*/
public static Component bindField(final Component cc, final Field fld){
return Monad.bind(autowired(cc), new FieldBinder(fld)).label(""+cc+".<"+fld+">")
.guard();
}
/**
* Creates a Component that will read a certain field
* from the instance created by another Component.
* The return value of the field is used as the result of the
* created Component.
* @param cc the Component to create the instance.
* @param name the field name.
* @param suppress_security whether access to non-public field is allowed.
* @return the new Component object.
* @throws IllegalArgumentException if the static resolution of the field
* fails.
*/
public static Component bindField(final Component cc, final String name,
boolean suppress_security)
throws IllegalArgumentException{
final String lbl = ""+cc+"."+name;
final Class type = cc.getType();
return Monad.bind(autowired(cc), readingField(type, name, suppress_security))
.label(lbl).guard();
}
/**
* Create a ComponentBinder object that invokes a method against the
* bound object.
* @param type the type to search the method against. Null defers the
* search to runtime.
* @param name the method name.
* @param suppress_security Are non-public methods searched at all?
* @return the ComponentBinder object.
*/
public static ComponentBinder invokingMethod(Class type, String name,
final boolean suppress_security){
if(type==null)
return new MethodNameBinder(name,suppress_security);
else
return new MethodBinder(ReflectionUtil.getMethod(type, name, suppress_security));
}
/**
* Create a ComponentBinder object that invokes a method against the
* bound object.
* @param type the type to search the method against. Null defers the
* search to runtime.
* @param name the method name.
* @param param_count the number of formal parameters.
* @param suppress_security Are non-public methods searched at all?
* @return the ComponentBinder object.
*/
public static ComponentBinder invokingMethod(Class type, String name,
int param_count, final boolean suppress_security){
if(type==null)
return new MethodNameParamCountBinder(name,param_count,suppress_security);
else
return new MethodBinder(ReflectionUtil.getMethod(type, name,
param_count, suppress_security));
}
/**
* Create a ComponentBinder object that reads a field value from
* the bound object.
* @param type the type to search the field against. Null defers the search
* to runtime.
* @param name the field name.
* @param suppress_security Are non-public fields searched at all?
* @return the ComponentBinder object.
*/
public static ComponentBinder readingField(Class type, String name,
boolean suppress_security){
if(type==null)
return new FieldNameBinder(name, suppress_security);
else
return new FieldBinder(ReflectionUtil.getField(type, name, suppress_security));
}
/**
* Creates a Component that will call a certain method
* against the instance created by another Component.
* The return value of the method is used as the result of the
* created Component.
* <br>
* If the type of the first component is statically known (getType()!=null),
* The method is statically resolved when bindMethod() is called.
* Otherwise, it is resolved when the actual component instantiation happens.
* @param cc the Component to create the instance.
* @param name the method name.
* @param param_types the parameter types of the method. null indicates
* a parameter-less method.
* @return the new Component object.
* @throws IllegalArgumentException if the static resolution of the method
* fails.
*/
public static Component bindMethod(final Component cc,
final String name, final Class[] param_types, boolean suppress_security)
throws IllegalArgumentException{
final String lbl =
""+cc+"."+name + jfun.util.StringUtils.listString("(",",",")",param_types);
final Class type = cc.getType();
return Monad.bind(autowired(cc),
invokingMethod(type, name, param_types, suppress_security))
.label(lbl).guard();
}
/**
* To create a ComponentBinder object that invokes a method
* against the bound object.
* @param type the type to search the method against. Null defers the search
* to runtime.
* @param name the method name.
* @param param_types the parameter types.
* @param suppress_security are non-public methods considered at all?
* @return the ComponentBinder object.
*/
public static ComponentBinder invokingMethod(Class type, String name,
Class[] param_types, boolean suppress_security){
if(type==null){
return new DynamicMethodBinder(name, param_types, suppress_security);
}
else
return new MethodBinder(ReflectionUtil.getMethod(type, name, param_types,
suppress_security));
}
/**
* Create a Component that will return a bean property
* of the instance created by another component.
* @param cc the component to create the instance.
* @param name the property name.
* @return the new Component object.
* @throws IntrospectionException if introspection fails.
*/
public static Component bindGetter(final Component cc,
final String name)throws IntrospectionException{
final String lbl = ""+cc+"."+name;
final Class type = cc.getType();
return Monad.bind(autowired(cc), invokingGetter(type, name))
.label(lbl).guard();
}
/**
* To create a ComponentBinder object that invokes a Java Bean getter
* against the bound object.
* @param type the type to introspect for the property. Null defers the
* introspection to runtime.
* @param name the property name.
* @return the ComponentBinder object.
* @throws IntrospectionException when bean introspection fails.
*/
public static ComponentBinder invokingGetter(Class type, String name)
throws IntrospectionException{
if(type==null){
return new DynamicPropertyReadingBinder(name);
}
else
return new PropertyReadingBinder(BeanType.instance(type), name);
}
/**
* Create a Component that will set a bean property
* of the instance created by another component.
* <br>
* The bean instance will still be used as the result.
* @param cc the component to create the instance.
* @param name the property name.
* @return the new Component object.
* @throws IntrospectionException if introspection fails.
*/
public static Component bindSetter(final Component cc,
final String name)
throws IntrospectionException{
final String lbl = ""+cc+"."+name+"=";
final Class type = cc.getType();
return Monad.bind(autowired(cc), invokingSetter(type,name))
.label(lbl).guard();
}
/**
* Create a ComponentBinder object that will invoke a Java Bean setter
* against the bound object.
* @param type the type to introspect for the property. Null defers the
* introspection to runtime.
* @param name the property name.
* @return the ComponentBinder object
* @throws IntrospectionException when bean introspection fails.
*/
public static <T> ComponentBinder<T,T> invokingSetter(Class<T> type, String name)
throws IntrospectionException{
if(type==null){
return new DynamicPropertyWritingBinder<T>(name);
}
else
return new PropertyWritingBinder(BeanType.instance(type), name);
}
/**
* Create a Component that will get an indexed property
* of the Java Bean instance created by another component.
* @param cc the component to create the instance.
* @param name the property name.
* @param ind the index.
* @return the new Component object.
* @throws IntrospectionException if introspection fails.
*/
public static Component bindGetter(final Component cc,
final String name, final int ind)throws IntrospectionException{
final String lbl = ""+cc+"."+name+"["+ind+"]";
final Class type = cc.getType();
return Monad.bind(autowired(cc), getterBinder(type, name, ind))
.label(lbl).guard();
}
private static ComponentBinder getterBinder(Class type,
final String name, final int ind)
throws IntrospectionException{
if(type==null){
return new DynamicIndexedPropertyReadingBinder(name, ind);
}
else
return new IndexedPropertyReadingBinder(BeanType.instance(type), name, ind);
}
/**
* Create a Component that will set an indexed property
* of the Java Bean instance created by another component.
* <br>
* The bean instance will still be used as the result.
* @param cc the component to create the instance.
* @param name the property name.
* @param ind the index.
* @return the new Component object.
* @throws IntrospectionException if introspection fails.
*/
public static <T> Component<T> bindSetter(final Component<T> cc,
final String name, final int ind)throws IntrospectionException{
final String lbl = ""+cc+"."+name+"["+ind+"]=";
final Class type = cc.getType();
return Monad.bind(autowired(cc), setterBinder(type, name, ind))
.label(lbl).guard();
}
private static <T> ComponentBinder<T,T> setterBinder(Class<T> type,
String name, int ind)
throws IntrospectionException{
if(type==null){
return new DynamicIndexedPropertyWritingBinder<T>(name, ind);
}
else
return new IndexedPropertyWritingBinder(BeanType.instance(type), name, ind);
}
/**
* Create a new Component object that utilizes singleton pattern.
* The component instance is cached the first time when the create()
* method is called, and the same instance is returned for subsequent calls.
* @param cc the Component object to create the component instance.
* @return the new Component object.
*/
public static <T> Component<T> singleton(Component<T> cc){
if(cc.isSingleton()) return cc;
return new SingletonComponent(cc);
}
/**
* Create a new Component object that utilizes singleton pattern
* within the scope of the provided Pool object.
* The component instance is cached the first time when the create()
* method is called, and the same instance is returned for subsequent calls.
* @param cc the Component object to create the component instance.
* @param scope the Pool object to specify the scope of the singleton pattern.
* @return the new Component object.
*/
public static <T> Component<T> singleton(Component<T> cc, Pool<T> scope){
return new PooledComponent(cc, scope);
}
/**
* Create a Component that instantiates a factory interface.
* All factory method in this factory interface will delegate
* to the component of the product to create the product.
* @param cc the product creator.
* @param factory_class the factory interface class.
* @param loader the class loader to load the proxy class.
* @param toString the string returned by the toString() method of the proxy.
* @return the factory component.
*/
public static <F> Component<F> factory(Creator cc, Class<F> factory_class,
ClassLoader loader, String toString){
final Component<F> r =
new FactoryComponent<F>(cc, factory_class, loader, toString).label(toString);
if(cc.isSingleton()){
return r.singleton();
}
else return r;
}
/**
* Create a Component that instantiates a factory interface.
* All factory methods in this factory interface will delegate
* to the component of the product to create the product.
* @param cc the product creator.
* @param factory_class the factory interface class.
* @param toString the string returned by the toString() method of the factory object.
* @return the factory component.
*/
public static <F> Component<F> factory(Creator cc, Class<F> factory_class,
String toString){
if(Factory.class.equals(factory_class)){
return factory(cc, toString);
}
return factory(cc, factory_class, factory_class.getClassLoader(),
toString);
}
/**
* Create a Component that instantiates the {@link Factory} interface.
* The {@link Factory#create()} method delegates
* to the component of the product to create the product.
* @param cc the component responsible for instantiation.
* @param toString the string returned by the toString() method of the factory.
* @return the factory component.
*/
public static <T> Component<Factory<T>> factory(Creator<T> cc, String toString){
return new TheFactoryComponent<T>(cc, toString).label(toString);
}
static <T> Component<T> ensureComponent(Component<T> cc, Object key){
if(cc==null)
throw new UnresolvedComponentException(key);
return cc;
}
/**
* Adapt a LazyComponent object to Component.
*
* @param lcc the LazyComponent object.
* @param key the name of the to-be-resolved component.
* @return the new Component object.
*/
public static <T> Component<T> fromLazy(final LazyComponent<T> lcc, final Object key){
return new LazyComponent2Component<T>(lcc, key).guard();
}
/**
* Create a Component object that delegates to another component
* identified by a key in a container.
* <br>
* When create() method is called,
* the getComponent() method is called against the target container to resolve the component.
* <br>
* This is a late-bound component because its type is resolved when create()
* method is called.
* @param cmap the container in which to resolve the component.
* @param key the key of the component to delegate to.
* @return the new Component object.
*/
public static Component useKey(
final ComponentMap cmap, final Object key){
return fromLazy(new LazyComponent(){
public Component eval(){
return cmap.getComponent(key);
}
public Class getType(){return null;}
public String toString(){return "useKey <"+key+">";}
}, key).label();
}
/**
* Create a component that uses a particular property value
* of the parent component as its instance.
* <br>
* If a component is being instantiated as a property or parameter
* of another component, then that other component is the parent
* component in this context.
* @param component_type the type of the component.
* @param key the property key.
* @param type the property type.
* @return the Component object.
*/
public static <T> Component<T> useProperty(
final Class component_type, final Object key, final Class<T> type){
return new UsePropertyComponent<T>(component_type, key, type).guard().label();
}
/**
* Create a component that uses a particular argument value
* of the parent component as its instance.
* <br>
* If a component is being instantiated as a property or parameter
* of another component, then that other component is the parent
* component in this context.
* @param src the function requesting the argument.
* @param pos the ordinal position of the argument.
* @param type the parameter type.
* @return the Component object.
*/
public static <T> Component<T> useArgument(
final Signature src, final int pos, final Class<T> type){
return new UseArgumentComponent<T>(src, pos, type).guard().label();
}
/**
* Create a Part object that redirects the resolution of a typed
* parameter/property to another container.
* @param cmap the container.
* @return the Part object.
*/
public static Part useContainer(final ComponentMap cmap){
return new Part(){
public Object create(Class type, Dependency dependency){
return ensureComponent(cmap.getComponentOfType(type), type)
.create(cmap.getDependencyOfType(type, cmap));
}
public Class verify(Class type, Dependency dependency){
return ensureComponent(cmap.getComponentOfType(type), type)
.verify(cmap.getDependencyOfType(type, cmap));
}
};
}
/**
* Create a Component object that returns the container object.
*/
public static Component<ComponentMap> thisContainer(){
return ThisContainerComponent.instance();
}
/**
* Create a Component object that delegates to another component in a container.
* <br>
* When create() method is called,
* the getComponentOfType() method is called against the target container
* to resolve the component.
* <br>
* This is a late-bound component because its type is resolved when create()
* method is called.
* @param cmap the container in which to resolve the component.
* @param type the component type to look for.
* @return the new Component object.
*/
public static <T> Component<T> useType(
final ComponentMap cmap, final Class<T> type){
return fromLazy(new LazyComponent<T>(){
public Component<T> eval(){
return cmap.getComponentOfType(type);
}
public Class<T> getType(){return null;}
public String toString(){
return "useType <"+Misc.getTypeName(type) +">";
}
}, type).guard().label();
}
/**
* Create a Component object that creates a java.util.List.
* <br>
* When create() method is called,
* the getComponentsOfType() method is called against the target container
* to resolve the components. The components found are invoked one by one
* to create component instances.
* Finally these instances are stored in a java.util.List object and returned
* as the return value.
* <br>
* @param cmap the container in which to resolve the components.
* @param type the component type to look for.
* @return the new Component object.
*/
public static <T> Component<ArrayList<T>> useAll(
final ComponentMap cmap, final Class<T> type){
return fromLazy(new LazyComponent<ArrayList<T>>(){
public Component<ArrayList<T>> eval(){
return list(cmap.getComponentsOfType(type));
}
public Class getType(){
return java.util.ArrayList.class;
}
public String toString(){
return "useAll <"+Misc.getTypeName(type)+">";
}
}, type).label();
}
/**
* Create a Component object that creates a java.util.List. <br>
* When method create() is called, it looks up in the target container
* for all the components with a state value that satisfies the given predicate.
* It then invokes these components one by one to create instances.
* These instances are finally stored in a java.util.List and returned
* as the return value.
* @param cmap the target container.
* @param pred the predicate to filter state value.
* @return the Component object.
*/
public static Component useState(
final ComponentMap cmap, final Predicate pred){
return fromLazy(new LazyComponent(){
public Component eval(){
return list(findComponents(cmap, pred));
}
public Class getType(){
return java.util.ArrayList.class;
}
public String toString(){
return "useState <"+pred+">";
}
}, pred).guard().label();
}
/**
* Label the frame for a Component so that whenever a YanException is thrown,
* the provided label will be populated in the resolution trace.
* @param cc the component to label.
* @param lbl the label.
* @return the new Component object.
*/
public static <T> Component<T> label(final Component<T> cc, final Object lbl){
return new ClosureableComponent(cc){
public Class verify(Dependency dp){
try{
return cc.verify(dp);
}
catch(YanException e){
e.push(lbl);
throw e;
}
}
public Object create(Dependency dp){
try{
return cc.create(dp);
}
catch(YanException e){
e.push(lbl);
throw e;
}
}
//label on a label immediately will ignore the previous label.
public Component label(Object lbl){
return cc.label(lbl);//Components.label(cc, lbl);
}
protected Component decorate(Component c){
return Components.label(c, lbl);
}
public String toString(){
return ""+lbl;
}
};
}
/**
* Create a Component object that delegates to another component
* identified by a key in the current container.
* <br>
* When create() method is called,
* the getComponent() method is called against the current container to resolve the component.
* <br>
* This is a late-bound component because its type is resolved when create()
* method is called.
* @param key the key of the component to delegate to.
* @return the new Component object.
*/
public static Component useKey(final Object key){
return new UseKeyComponent(key).guard().label();
}
/**
* Create a Component object that delegates to another component
* in the current container.
* <br>
* When create() method is called,
* the getComponentOfType() method is called against the current container
* to resolve the component.
* <br>
* This is a late-bound component because its type is resolved when create()
* method is called.
* @param type the component type to look for.
* @return the new Component object.
*/
public static <T> Component<T> useType(final Class<T> type){
return new UseTypeComponent<T>(type).guard().label();
}
/**
* Create a Component object that delegates to another component
* in the current container.
* <p>
* When create() method is called,
* the getComponentsOfType() method is called against the current container
* to resolve the component. If no component is found or more than one are found,
* it tries to search component using the alternative keys sequentially until
* a Component is found.
* </p>
* <p>
* UnresolvedComponentException is thrown if no Component is found at last.
* </p>
* This is a late-bound component because its type is resolved when create()
* method is called.
* @param type the component type to look for.
* @param alternative_keys the alternative keys to try when search by type failed.
* @return the new Component object.
*/
public static <T> Component<T> autodetect(final Class<T> type, final Object[] alternative_keys){
return new AutoDetectingComponent<T>(type, alternative_keys).guard().label();
}
/**
* Create a Component object that creates a java.util.List.
* <br>
* When create() method is called,
* the getComponentsOfType() method is called against the current container
* to resolve the components. The components found are invoked one by one
* to create component instances.
* Finally these instances are stored in a java.util.List object and returned
* as the return value.
* <br>
* @param type the component type to look for.
* @return the new Component object.
*/
public static <T> Component<List<T>> useAll(final Class<T> type){
return new Component<List<T>>(){
public Class verify(Dependency dependency){
return list(findComponents(dependency.getComponentMap(), type))
.verify(dependency);
}
public List<T> create(Dependency dependency){
return list(findComponents(dependency.getComponentMap(), type))
.create(dependency);
}
private java.util.List<Component<T>> findComponents(ComponentMap cmap,
final Class<T> type){
return cmap.getComponentsOfType(type);
}
public Class getType(){
return java.util.ArrayList.class;
}
public boolean isConcrete(){
return true;
}
public String toString(){
return "useAll <" + Misc.getTypeName(type)+">";
}
public boolean isSingleton(){
return false;
}
}.guard().label();
}
private static java.util.List<Component> findComponents(ComponentMap cmap,
final Predicate pred){
final java.util.Collection all = cmap.getComponents();
final java.util.ArrayList<Component> ret = new java.util.ArrayList<Component>();
for(java.util.Iterator it=all.iterator();it.hasNext();){
final Component c = (Component)it.next();
if(pred.isObject(c.getState())){
ret.add(c);
}
}
return ret;
}
/**
* Creates a Component that throws {@link jfun.yan.DefaultingException}.
* This exception will be caught later and a default action is taken
* according to the context. <br>
* For example, Component expecting a mandatory parameter
* will use null for default value; bean component will ignore
* setting the property; withDefaultArgument or withDefaultProperty
* will provide a default value.
* @return the new Component object.
*/
public static Component useDefault(){
return UseDefaultComponent.instance();
}
/**
* Create a Component object that creates a java.util.List. <br>
* When method create() is called, it looks up in the current container
* for all the components with a state value that satisfies the given predicate.
* It then invokes these components one by one to create instances.
* These instances are finally stored in a java.util.List and returned
* as the return value.
* @param pred the predicate to filter state value.
* @return the Component object.
*/
public static Component useState(final Predicate pred){
return new Component(){
public Class verify(Dependency dependency){
final ComponentMap cmap = dependency.getComponentMap();
return list(findComponents(cmap, pred)).verify(dependency);
}
public Object create(Dependency dependency){
final ComponentMap cmap = dependency.getComponentMap();
return list(findComponents(cmap, pred)).create(dependency);
}
public Class getType(){
return java.util.ArrayList.class;
}
public boolean isConcrete(){
return true;
}
public String toString(){
return "useState <" + pred +">";
}
public boolean isSingleton(){
return false;
}
}.guard().label();
}
/**
* Create a Component object that returns the state of a Stateful object
* as its component instance.
* @param st the Stateful object.
* @return the Component object.
*/
public static Component returnState(final Stateful st){
return new ReturnStateComponent(st).label();
}
/**
* Create a Component object that returns the type of a Typeful object
* as its component instance.
* @param t the Typeful object.
* @return the Component object.
*/
public static Component returnType(final Typeful t){
return new ReturnTypeComponent(t).guard().label();
}
/**
* Create a Component object that verifies the Verifiable object
* and returns the result type as return value.
* @param veri the Verifiable object.
* @return the Component object.
*/
public static Component returnVerification(final Verifiable veri){
return new ReturnVerificationComponent(veri).guard()
.label();
}
/*
private static Dependency setArguments(
final Dependency dependency,
final Creator[] factories){
return new DelegatingDependency(dependency){
public Object getArgument(int i, Class type){
if(i<factories.length){
final Object r = factories[i].create(dependency);
Utils.checkArg(getComponentKey(), i, type, r);
return r;
}
else return dependency.getArgument(i, type);
}
public Class verifyArgument(int i, Class type){
if(i<factories.length){
final Creator factory = factories[i];
final Class r = factory.verify(dependency);
Utils.checkType(getComponentKey(), i, type, r);
return r;
}
else return dependency.verifyArgument(i, type);
}
};
}
private static Dependency setProperties(
final Dependency dependency, final java.util.Map props){
return new DelegatingDependency(dependency){
public Object getProperty(Object k, Class type){
final Creator cr = (Creator)props.get(k);
if(cr != null){
final Object r = cr.create(dependency);
Utils.checkArg(getComponentKey(), k, type, r);
return r;
}
else return dependency.getProperty(k, type);
}
public Class verifyProperty(Object k, Class type){
final Creator cr = (Creator)props.get(k);
if(cr != null){
final Class r = cr.verify(dependency);
Utils.checkType(getComponentKey(), k, type, r);
return r;
}
else return dependency.verifyProperty(k, type);
}
};
}
private static Dependency setArgument(
final Dependency dependency,
final int k, final Creator factory){
return new DelegatingDependency(dependency){
public Object getArgument(int i, Class type){
if(i==k){
final Object r = factory.create(dependency);
Utils.checkArg(getComponentKey(), i, type, r);
return r;
}
else return dependency.getArgument(i, type);
}
public Class verifyArgument(int i, Class type){
if(i==k){
final Class r = factory.verify(dependency);
Utils.checkType(getComponentKey(), i, type, r);
return r;
}
else return dependency.verifyArgument(i, type);
}
};
}*/
private static Dependency setArguments(
final Dependency dependency,
final Part part){
return new DelegatingDependency(dependency){
public Object getArgument(Signature src, int i, Class type){
final Object r = part.create(type, dependency);
Utils.checkArg(src, getComponentKey(), i, type, r);
return r;
}
public Class verifyArgument(Signature src, int i, Class type){
final Class r = part.verify(type, dependency);
Utils.checkType(src, getComponentKey(), i, type, r);
return r;
}
public Dependency seal(){
return setArguments(dependency.seal(), part);
}
};
}
private static Dependency setProperties(
final Dependency dependency, final Part part){
return new DelegatingDependency(dependency){
public Object getProperty(Class component_type, Object k, Class type){
final Object r = part.create(type, dependency);
Utils.checkArg(component_type, getComponentKey(), k, type, r);
return r;
}
public Class verifyProperty(Class component_type, Object k, Class type){
final Class r = part.verify(type, dependency);
Utils.checkType(component_type, getComponentKey(), k, type, r);
return r;
}
public Dependency seal(){
return setProperties(dependency.seal(), part);
}
};
}
/**
* Create a ParameterBinder object using an array of Creator object
* that will be used to create arguments.
* @param args the creators for the arguments.
* @return the ParameterBinder object.
*/
static ParameterBinder getParameterBinder(final Creator[] args){
return new ParameterBinder(){
public Creator bind(final Signature src, int k, Class type){
if(k>=0 && k < args.length){
return args[k];
}
else return useArgument(src, k, type);
}
};
}
/**
* Create a PropertyBinder object using Creator objects
* stored in a java.util.Map object.
* These creator objects will be used to create property values.
* @param props the creators for the properties and their keys.
* @return the PropertyBinder object.
*/
static PropertyBinder getPropertyBinder(final java.util.Map props){
return new PropertyBinder(){
public Creator bind(Class component_type, Object k, Class type){
final Creator r = (Creator)props.get(k);
if(r==null) return useProperty(component_type, k, type);
else return r;
}
};
}
/**
* Customizes a Component's parameters with an array of Creator objects.
* For parameter i,
* creators[i] is used to create it if i is between 0 and factories.length.
* the default creation strategy is used otherwise.
* @param cc the Component to customize.
* @param creators the creator objects for creating arguments.
* @return the new Component object.
*/
public static Component withArguments(
final Component cc, final Creator... creators){
return bindArguments(cc, getParameterBinder(creators));
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setArguments(dependency, factories),
ctxt);
}
public Class verify(Dependency dependency){
return cc.verify(Components.setArguments(dependency, factories),
ctxt);
}
protected Component decorate(Component cc){
return Components.withArguments(cc, factories);
}
};*/
}
/**
* Create a sealed component.
* A sealed component assumes responsibility to
* resolve its own dependencies.
* It does not attempt to look up the container
* to resolve any unresolved dependency.
* <br>
* seal can be used to disable auto-wiring at component level.
* @param cc the component to seal.
* @return the new Component object.
*/
public static Component seal(final Component cc){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(dependency.seal());
}
public Class verify(Dependency dependency){
return cc.verify(dependency.seal());
}
public String toString(){
return "seal <" + cc + ">";
}
protected Component decorate(Component cc){
return Components.seal(cc);
}
public Component seal(){
return this;
}
}.label();
}
/**
* To create a Component that ignores all subsequent
* customizations to the parameters or properties.
* @param c the Component to decorate.
* @return the new Component that's guaranteed to auto-wire.
*/
public static Component autowired(final Component c){
return new ClosureableComponent(c){
public Object create(Dependency dependency){
return c.create(dependency.getOriginal());
}
public Class verify(Dependency dependency){
return c.verify(dependency.getOriginal());
}
public String toString(){
return "autowired <" + c + ">";
}
protected Component decorate(Component cc){
return Components.autowired(cc);
}
}.label();
}
/**
* Customize the arguments of a component with a
* {@link jfun.yan.ParameterBinder} object.
* @param cc the component.
* @param binder the ParameterBinder object.
* @return the new Component.
*/
public static Component bindArguments(
final Component cc, final ParameterBinder binder){
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.bindArguments(dependency, binder));
}
public Class verify(Dependency dependency){
return cc.verify(Components.bindArguments(dependency, binder));
}
//additionally cache the result at the top level to avoid creation of Dependency objects.
public Component singleton(){
return new SingletonComponent(super.singleton());
}
//additionally cache the result at the top level to avoid creation of Dependency objects.
public Component singleton(Pool scope){
return new PooledComponent(super.singleton(scope), scope);
}
protected Component decorate(Component cc){
return cc.bindArguments(binder);
}
};
}
/**
* Customize one argument of a component with a
* {@link jfun.yan.ParameterBinder} object.
* @param cc the component.
* @param k the ordinal position of the parameter.
* @param binder the ParameterBinder object.
* @return the new Component.
*/
public static Component bindArgument(
final Component cc, final int k, final ParameterBinder binder){
return bindArguments(cc, new ParameterBinder(){
public Creator bind(final Signature src, int i, Class type){
if(i==k){
return binder.bind(src, i, type);
}
else return useArgument(src, i, type);
}
});
}
/**
* Customizes a Component's properties with provided Creator objects
* stored in a java.util.Map object.
* @param cc the Component object to customize.
* @param props the map that contains mappings between property keys
* and Creator objects that creates the property value.
* This Map object is not modified within this method.
* It is allowed to subsequently change the Map object
* after this method is called.
* The new change will be picked up by the component
* when creating instances.
* If a property's key is not contained in the Map object,
* the creation strategy is not changed for this property.
* @return the new Component object.
*/
public static Component withProperties(
final Component cc, final java.util.Map props){
return bindProperties(cc, getPropertyBinder(props));
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setProperties(dependency, props));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setProperties(dependency, props));
}
public Component decorate(Component c){
return Components.withProperties(c, props);
}
};*/
}
/**
* Customize the properties of a component with a
* {@link jfun.yan.PropertyBinder} object.
* @param cc the component.
* @param binder the PropertyBinder object.
* @return the new Component.
*/
public static Component bindProperties(
final Component cc, final PropertyBinder binder){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.bindProperties(dependency, binder));
}
public Class verify(Dependency dependency){
return cc.verify(Components.bindProperties(dependency, binder));
}
//additionally cache the result at the top level to avoid creation of Dependency objects.
public Component singleton(){
return new SingletonComponent(super.singleton());
}
//additionally cache the result at the top level to avoid creation of Dependency objects.
public Component singleton(Pool scope){
return new PooledComponent(super.singleton(scope), scope);
}
public Component decorate(Component c){
return c.bindProperties(binder);
}
};
}
/**
* Customize the properties of a component with a
* {@link jfun.yan.PropertyBinder} object.
* @param cc the component.
* @param k the property key.
* @param binder the PropertyBinder object.
* @return the new Component.
*/
public static Component bindProperty(final Component cc,
final Object k, final PropertyBinder binder){
return bindProperties(cc, new PropertyBinder(){
public Creator bind(Class component_type, Object i, Class type){
if(i.equals(k)){
return binder.bind(component_type, i,type);
}
else return useProperty(component_type, i, type);
}
});
}
/**
* Customizes a Component's properties with the provided Creator objects.
* For property identified by keys[i], creators[i] is used for creating value.
* The keys array and creators array has to be the same length.
* @param cc the Component object to customize.
* @param names the property keys. Duplicate key is not allowed in this array.
* Names are case-sensitive.
* @param creators the creators for creating property values.
* @return the new Component object.
*/
public static Component withProperties(
final Component cc, final String[] names, final Creator[] creators){
return withProperties(cc, Utils.hashmap(names, creators));
}
/**
* Redirects resolution of properties to arguments.
* So that a property-based component (such as bean)
* can be used as if it were a parameter-based on,
* for which withArgument, withArguments can be used to customize.
* <br>
* It is useful when applying a property-based component to an algorithm
* that relies on argument-based component.
* The factory() combinator, for example, relies on parameter-based component.
* @param cc the component to be redirected.
* @param keys the property keys to redirect.
* The ordinal position of each key indicates the position of the parameter.
* @return the redirected Component object.
*/
public static Component fromArguments(
final Component cc, final Object... keys){
return bindProperties(cc, new PropertyBinder(){
public Creator bind(Class component_type, Object key, Class type){
final int ind = findKey(key);
if(ind < 0) return useProperty(component_type, key, type);
else{
final Signature sig = new Property2Signature(component_type, key, type);
return useArgument(sig, ind, type);
}
}
private int findKey(Object k){
for(int i=0; i<keys.length;i++){
if(Utils.eq(k, keys[i])) return i;
}
return -1;
}
});
}
/**
* Redirects resolution of arguments to properties.
* So that a parameter-based component (such as ctor or method)
* can be used as if it were a bean component,
* for which withProperty, withProperties can be used to customize.
* <br>
* It is useful when applying a parameter-based component to an algorithm
* that relies on property-based component.
* @param cc the component to be redirected.
* @param keys the property keys for parameters.
* The ordinal position of each key indicates the position of the parameter.
* @return the redirected Component object.
*/
public static Component fromProperties(
final Component cc, final Object... keys){
return bindArguments(cc, new ParameterBinder(){
public Creator bind(final Signature src, int k, Class type){
if(k<0 || k>=keys.length){
return useArgument(src, k, type);
}
else return useProperty(src.getReturnType(), keys[k], type);
}
});
}
/**
* Customizes a Component with a Part object to create all its parameters.
* @param cc the Component object to customize.
* @param part the Part object.
* @return the new Component object.
*/
public static Component withArguments(
final Component cc, final Part part){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setArguments(dependency, part));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setArguments(dependency, part));
}
protected Component decorate(Component cc){
return Components.withArguments(cc, part);
}
};
}
/**
* Customizes a Component object with a Part object to create all its property values.
* @param cc the Component object to customize.
* @param part the Part object.
* @return the new Component object.
*/
public static Component withProperties(
final Component cc, final Part part){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setProperties(dependency, part));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setProperties(dependency, part));
}
public Component decorate(Component c){
return Components.withProperties(c, part);
}
};
}
/*
private static Dependency sealDependency(Dependency dep){
return new DelegatingDependency(dep){
public Object getArgument(Signature src, int i, Class type){
throw new IrresolveableArgumentException(getComponentKey(), i, type);
}
public Object getProperty(Class component_type, Object local_key, Class type){
throw new IrresolveablePropertyException(getComponentKey(), local_key, type);
}
public Class verifyArgument(Signature src, int i, Class type){
throw new IrresolveableArgumentException(getComponentKey(), i, type);
}
public Class verifyProperty(Class component_type, Object local_key, Class type){
throw new IrresolveablePropertyException(getComponentKey(), local_key, type);
}
public Dependency seal(){return this;}
};
}*/
private static Dependency setParent(final Dependency dep,
final Dependency parent){
return new DelegatingDependency(dep){;
public Dependency getParent(){
return parent;
}
public Dependency seal(){
return setParent(dep.seal(), parent);
}
};
}
private static Dependency ofPart(final Dependency dep, Class type){
final ComponentMap cmap = dep.getComponentMap();
final Dependency newdep = cmap.getDependencyOfType(type, cmap);
return setParent(newdep, dep);
}
private static Dependency ofProperty(final Dependency dep, Object key, Class type){
return ofPart(dep, type);
}
private static Dependency ofParameter(Dependency dep, int ind, Class type){
return ofPart(dep, type);
}
/**
* Customize a Dependency object with a ParameterBinder object.
* @param dependency the dependency object.
* @param binder the ParameterBinder object.
* @return the new Dependency object.
*/
public static Dependency bindArguments(
final Dependency dependency,
final ParameterBinder binder){
return new DelegatingDependency(dependency){
public Object getArgument(Signature src, int i, Class type){
final Object r = binder.bind(src, i, type)
.create(ofParameter(dependency, i, type));
Utils.checkArg(src, getComponentKey(), i, type, r);
return r;
}
public Class verifyArgument(Signature src, int i, Class type){
final Class r = binder.bind(src, i,type)
.verify(ofParameter(dependency, i, type));
Utils.checkType(src, getComponentKey(), i, type, r);
return r;
}
public Dependency seal(){
return bindArguments(dependency.seal(), binder);
}
};
}
/**
* Customize a Dependency object with a PropertyBinder object.
* @param dependency the dependency object.
* @param binder the PropertyBinder object.
* @return the new Dependency object.
*/
public static Dependency bindProperties(
final Dependency dependency,
final PropertyBinder binder){
return new DelegatingDependency(dependency){
public Object getProperty(Class component_type, Object i, Class type){
final Object r = binder.bind(component_type, i,type)
.create(ofProperty(dependency, i, type));
Utils.checkArg(component_type, getComponentKey(), i, type, r);
return r;
}
public Class verifyProperty(Class component_type, Object i, Class type){
final Class r = binder.bind(component_type, i, type)
.verify(ofProperty(dependency, i, type));
Utils.checkType(component_type, getComponentKey(), i, type, r);
return r;
}
public Dependency seal(){
return bindProperties(dependency.seal(), binder);
}
};
}
/*
private static Dependency setProperty(
final Dependency dependency,
final Object k, final Creator factory){
return new DelegatingDependency(dependency){
public Object getProperty(Object i, Class type){
if(i.equals(k)){
final Object r = factory.create(dependency);
Utils.checkArg(getComponentKey(), i, type, r);
return r;
}
else return dependency.getProperty(i, type);
}
public Class verifyProperty(Object i, Class type){
if(i.equals(k)){
final Class r = factory.verify(dependency);
Utils.checkType(getComponentKey(), i, type, r);
return r;
}
else return dependency.verifyProperty(i, type);
}
};
}
private static Dependency withDefaultProperty(
final Dependency dependency,
final Object k, final Creator factory){
return new DelegatingDependency(dependency){
public Object getProperty(Object i, Class type){
try{
return dependency.getProperty(i, type);
}
catch(DefaultingException e){
if(i.equals(k)){
Object r = factory.create(dependency);
Utils.checkArg(getComponentKey(), i, type, r);
return r;
}
else throw e;
}
}
public Class verifyProperty(Object i, Class type){
try{
return dependency.verifyProperty(i, type);
}
catch(DefaultingException e){
if(i.equals(k)){
final Class r = factory.verify(dependency);
Utils.checkType(getComponentKey(), i, type, r);
return r;
}
else throw e;
}
}
};
}
private static Dependency withDefaultArgument(
final Dependency dependency,
final int k, final Creator factory){
return new DelegatingDependency(dependency){
public Object getArgument(int i, Class type){
Object r = dependency.getArgument(i, type);
if(r==default_value() && i == k){
r = factory.create(dependency);
Utils.checkArg(getComponentKey(), i, type, r);
}
return r;
}
public Class verifyArgument(int i, Class type){
Class r = dependency.verifyArgument(i, type);
if(default_value().getClass().equals(r) && i == k){
r = factory.verify(dependency);
Utils.checkType(getComponentKey(), i, type, r);
}
return r;
}
};
}
private static boolean isPropertyIgnored(Class t){
return getDefaultValueType().equals(t);
}
private static Class getDefaultValueType(){
return default_value().getClass();
}
private static Class tryVerify(final Dependency dep, final Object key,
final Class type){
try{
return dep.verifyProperty(key, type);
}
catch(ComponentResolutionException e){
return getDefaultValueType();
}
}
private static Class tryVerify(final Dependency dep, final int i,
Class type){
try{
return dep.verifyArgument(i, type);
}
catch(ComponentResolutionException e){
return getDefaultValueType();
}
}
private static Object defaulting(Dependency dep, Object i,
Class type){
if(isPropertyIgnored(tryVerify(dep,
i, type))){
return default_value();
}
else return dep.getProperty(i, type);
}
private static Object defaulting(Dependency dep, int i,
Class type){
if(isPropertyIgnored(tryVerify(dep, i, type)))
return default_value();
else
return dep.getArgument(i, type);
}
private static Dependency optionalProperties(final Dependency dependency){
return new DelegatingDependency(dependency){
public Object getProperty(Object i, Class type){
return defaulting(getDelegateTarget(), i, type);
}
public Class verifyProperty(Object i, Class type){
return tryVerify(getDelegateTarget(), i, type);
}
};
}
private static Dependency optionalParameters(final Dependency dependency){
return new DelegatingDependency(dependency){
public Object getArgument(int i, Class type){
return defaulting(getDelegateTarget(), i, type);
}
public Class verifyArgument(int i, Class type){
return tryVerify(getDelegateTarget(), i, type);
}
};
}
private static Dependency optionalProperty(
final Dependency dependency,
final Object k){
return new DelegatingDependency(dependency){
public Object getProperty(Object i, Class type){
if(i.equals(k)){
return defaulting(getDelegateTarget(), i, type);
}
else return dependency.getProperty(i, type);
}
public Class verifyProperty(Object i, Class type){
if(i.equals(k)){
return tryVerify(getDelegateTarget(), i, type);
}
else return dependency.verifyProperty(i, type);
}
};
}
private static Dependency optionalParameter(
final Dependency dependency,
final int k){
return new DelegatingDependency(dependency){
public Object getArgument(int i, Class type){
if(i == k){
return defaulting(getDelegateTarget(), i, type);
}
else return dependency.getArgument(i, type);
}
public Class verifyArgument(int i, Class type){
if(i==k){
return tryVerify(getDelegateTarget(), i, type);
}
return dependency.verifyArgument(i, type);
}
};
}
private static Dependency ignoreProperty(
final Dependency dependency,
final Object k){
return new DelegatingDependency(dependency){
public Object getProperty(Object i, Class type){
if(i.equals(k)){
return default_value();
}
return dependency.getProperty(i, type);
}
public Class verifyProperty(Object i, Class type){
if(i.equals(k)){
return type;
}
return dependency.verifyProperty(i, type);
}
};
}*/
private static Dependency setArgument(
final Dependency dependency,
final int k, final Part part){
return new DelegatingDependency(dependency){
public Object getArgument(Signature src, int i, Class type){
if(i==k){
final Object r = part.create(type, dependency);
Utils.checkArg(src, getComponentKey(), i, type, r);
return r;
}
else return dependency.getArgument(src, i, type);
}
public Class verifyArgument(Signature src, int i, Class type){
if(i==k){
final Class r = part.verify(type, dependency);
Utils.checkType(src, getComponentKey(), i, type, r);
return r;
}
else return dependency.verifyArgument(src, i, type);
}
public Dependency seal(){
return setArgument(dependency.seal(), k, part);
}
};
}
private static Dependency setProperty(
final Dependency dependency,
final Object k, final Part part){
return new DelegatingDependency(dependency){
public Object getProperty(Class component_type, Object i, Class type){
if(i.equals(k)){
final Object r = part.create(type, dependency);
Utils.checkArg(component_type, getComponentKey(), i, type, r);
return r;
}
else return dependency.getProperty(component_type, i, type);
}
public Class verifyProperty(Class component_type, Object i, Class type){
if(i.equals(k)){
final Class r = part.verify(type, dependency);
Utils.checkType(component_type, getComponentKey(), i, type, r);
return r;
}
else return dependency.verifyProperty(component_type, i, type);
}
public Dependency seal(){
return setProperty(dependency.seal(), k, part);
}
};
}
/**
* Customizes the creation of one parameter of a Component object
* using a Creator object.
* @param cc the Component object to customize.
* @param k the ordinal position of the parameter.
* @param factory the creator object for creating that argument.
* @return the new Component object.
*/
public static Component withArgument(
final Component cc,
final int k, final Creator factory){
return bindArgument(cc, k, constParamBinder(factory));
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setArgument(dependency, k, factory));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setArgument(dependency, k, factory));
}
public Component decorate(Component c){
return Components.withArgument(c, k, factory);
}
};*/
}
private static ParameterBinder constParamBinder(final Creator c){
return new ParameterBinder(){
public Creator bind(Signature src, int k, Class type){
return c;
}
};
}
private static PropertyBinder constPropertyBinder(final Creator c){
return new PropertyBinder(){
public Creator bind(Class component_type, Object k, Class type){
return c;
}
};
}
/**
* Customizes the creation of one property value of a Component object
* with a Creator object.
* @param cc the Component object to customize.
* @param k the property key.
* @param factory the Creator object.
* @return the new Component object.
*/
public static <T> Component<T> withProperty(
final Component<T> cc,
final Object k, final Creator factory){
return bindProperty(cc, k, constPropertyBinder(factory));
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setProperty(dependency, k, factory));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setProperty(dependency, k, factory));
}
public Component decorate(Component c){
return Components.withProperty(c, k, factory);
}
};*/
}
/**
* Customizes the creation of one parameter of a Component object
* with a Part object.
* @param cc the Component object to customize.
* @param part the Part object.
* @return the new Component object.
*/
public static <T> Component<T> withArgument(
final Component<T> cc,
final int k, final Part part){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setArgument(dependency, k, part));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setArgument(dependency, k, part));
}
public Component decorate(Component c){
return Components.withArgument(c, k, part);
}
};
}
/**
* Customizes the creation of one property value of a Component object
* with a Part object.
* @param cc the Component object to customize.
* @param k the property key.
* @param part the Part object.
* @return the new Component object.
*/
public static <T> Component<T> withProperty(
final Component<T> cc,
final Object k, final Part part){
return new ClosureableComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.setProperty(dependency, k, part));
}
public Class verify(Dependency dependency){
return cc.verify(Components.setProperty(dependency, k, part));
}
public Component decorate(Component c){
return Components.withProperty(c, k, part);
}
};
}
/**
* Creates a new Component object that makes a property identified by a key
* optional. <br>
* When a component sees a property is passed in with the special
* default value indicator, it skips the property.
* <br>
* However, this behavior can be changed when withDefaultProperty()
* is used. In that case, the specified alternative Creator object
* will be used to create the default value.
* <br>
* Technically, when UnresolvedComponentException
* or UnsatisfiedComponentException is caught,
* defaulting takes place.
* @param cc the Component object.
* @param k the property key to make optional.
* @return the new Component object.
*/
public static <T> Component<T> optionalProperty(
final Component<T> cc, final Object k){
return bindProperty(cc, k, _optional_prop);
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.optionalProperty(dependency, k));
}
public Class verify(Dependency dependency){
return cc.verify(Components.optionalProperty(dependency, k));
}
public Component decorate(Component c){
return Components.optionalProperty(c, k);
}
};*/
}
/**
* Make sure the properties of a Java bean component are optional.
* @param cc the bean component.
* @return the new Component.
*/
public static <T> Component<T> optionalProperties(final Component<T> cc){
return bindProperties(cc, _optional_prop);
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.optionalProperties(dependency));
}
public Class verify(Dependency dependency){
return cc.verify(Components.optionalProperties(dependency));
}
public Component optionalProperties(){return this;}
public Component decorate(Component c){
return Components.optionalProperties(c);
}
};*/
}
private static final ParameterBinder _optional_param = new ParameterBinder(){
public Creator bind(Signature src, int i, Class type){
return useArgument(src, i, type).optional();
}
};
private static final PropertyBinder _optional_prop = new PropertyBinder(){
public Creator bind(Class component_type, Object k, Class type){
return useProperty(component_type, k, type).optional();
}
};
/**
* Make sure the parameters of a component are optional.
* @param cc the component object.
* @return the new component object.
*/
public static <T> Component<T> optionalParameters(final Component<T> cc){
return bindArguments(cc, _optional_param);
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.optionalParameters(dependency));
}
public Class verify(Dependency dependency){
return cc.verify(Components.optionalParameters(dependency));
}
public Component optionalParameters(){return this;}
public Component decorate(Component c){
return Components.optionalParameters(c);
}
};*/
}
/**
* Creates a new Component object that makes a parameter
* optional.
* <br>
* Technically, when UnresolvedComponentException
* or UnsatisfiedComponentException is caught,
* defaulting takes place.
* <br>
* The default value is normally null. But when
* withDefaultArgument() is used, it uses the value returned from the default Creator object.
* @param cc the Component object.
* @param k the ordinal position of the parameter.
* @return the new Component object.
*/
public static <T> Component<T> optionalParameter(
final Component<T> cc, final int k){
return bindArgument(cc, k, _optional_param);
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.optionalParameter(dependency, k));
}
public Class verify(Dependency dependency){
return cc.verify(Components.optionalParameter(dependency, k));
}
public Component decorate(Component c){
return Components.optionalParameter(c, k);
}
};*/
}
/**
* Create a Component object that will use an alternative Creator object
* when a certain property cannot be resolved or is passed in as default.
*
* @param cc the original Compoenent object.
* @param k the property key.
* @param v the alternative Creator object.
* @return the new Component object.
*/
public static <T> Component<T> withDefaultProperty(final Component<T> cc,
final Object k, final Creator v){
final Recovery def =
Monad.onException(DefaultingException.class, v);
return bindProperty(cc, k, new PropertyBinder(){
public Creator bind(Class component_type, Object i, Class type){
return
Monad.recover(
Monad.mplus(useProperty(component_type, i, type), v),
def
);
}
});//cc.withProperty(k))
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.withDefaultProperty(dependency, k, v));
}
public Class verify(Dependency dependency){
return cc.verify(Components.withDefaultProperty(dependency, k, v));
}
public Component decorate(Component c){
return Components.withDefaultProperty(c, k, v);
}
};*/
}
/**
* Create a Component object that will use an alternative Creator object
* when a certain parameter cannot be resolved
* or is passed in as default.
*
* @param cc the original Compoenent object.
* @param k the ordinal position of the parameter.
* @param v the alternative Creator object.
* @return the new Component object.
*/
public static <T> Component<T> withDefaultArgument(final Component<T> cc,
final int k, final Creator v){
return bindArgument(cc, k, new ParameterBinder(){
final Recovery def = Monad.onException(DefaultingException.class, v);
public Creator bind(Signature src, int i, Class type){
return Monad.recover(
Monad.mplus(useArgument(src, i, type), v),
def
);
}
});
/*
return new DecoratingComponent(cc){
public Object create(Dependency dependency){
return cc.create(Components.withDefaultArgument(dependency, k, v));
}
public Class verify(Dependency dependency){
return cc.verify(Components.withDefaultArgument(dependency, k, v));
}
public Component decorate(Component c){
return Components.withDefaultArgument(c, k, v);
}
};*/
}
/**
* Customizes the creation of properties and parameters of a Component object
* with a Dependency object.
* @param cc the Component object to customize.
* @param dependency the Dependency object.
* @return the new Component object.
*/
static <T> Component<T> withDependency(
final Component<T> cc, final Dependency dep){
return new ClosureableComponent(cc){
public Object create(Dependency d){
return cc.create(dep);
}
public Class verify(Dependency d){
return cc.verify(dep);
}
public Component decorate(Component c){
return Components.withDependency(c, dep);
}
};
}
/**
* To create a Component that returns an element of the array/list
* instance returned from another Component object.
* @param c the Component object returning array or list.
* @param ind the subscript.
* @return the Component object.
*/
public static Component subscript(final Component c, final int ind){
final Component result = c.map(new Map(){
public Object map(Object a){
if(a==null)
throw new IllegalArgumentException("subscript on null pointer");
else if(a.getClass().isArray()){
return Array.get(a, ind);
}
else if(a instanceof List){
return ((List)a).get(ind);
}
else
throw new IllegalArgumentException("cannot apply subscript against "
+ Misc.getTypeName(a.getClass()));
}
});
return result;
}
/**
* To create a Component object that calls method "get" against
* the java.util.Map object instantiated by another Component.
* @param c the Component object that instantiates a java.util.Map.
* @param key the key.
* @return the Component object that returns the value corresponding to the key.
*/
public static Component get(Component c, final Object key){
return c.map(new Map(){
public Object map(Object o){
if(o==null)
throw new IllegalArgumentException("cannot apply get against null");
if(o instanceof java.util.Map){
return ((java.util.Map)o).get(key);
}
else
throw new IllegalArgumentException("cannot apply get against "
+ Misc.getTypeName(o.getClass()));
}
});
}
/**
* Decorate a Component object so that
* the new Component object guards against infinite dependency loop.
* @return the new Component object.
*/
public static <T> Component<T> guard(Component<T> cc){
if(cc.isSingleton()){
//singleton do not need guarding.
return cc;
}
return new GuardedComponent<T>(cc);
}
/**
* Customizes a Component object with the specified user state.
* @param cc the Component object to customize.
* @param obj the user state object.
* @return the new Component object.
*/
public static <T> Component<T> withState(Component<T> cc, Object obj){
return new StatefulComponent(cc, obj);
}
/**
* Customizes a Component object with a new component type.
* If the component is not late-bound (where getType() returns null),
* the given type has to be same or super type of the component being customized.
* @param cc the Component object.
* @param type the target type.
* @return the new Component object.
*/
public static <Super, T extends Super> Component<Super> subsume(final Component<T> cc, final Class<Super> type){
final Class subtype = cc.getType();
if(subtype!=null){
checkType(type, subtype);
if(type.equals(subtype)){
return (Component<Super>)cc;
}
}
return new TypedComponent(cc, type){
protected Component decorate(Component c){
return Components.subsume(c, type);
}
public String toString(){
return "subsume <" + cc + "> as " + Misc.getTypeName(type);
}
}.label();
}
/**
* Decorate a Factory so that the return type is guaranteed to be
* of a certain type.
* @param type the expected type.
* @param f the decorated Factory object.
* @return the new Factory object.
*/
private static <T> Factory<T> castFactory(final Class<T> type, final Factory<?> f){
return new Factory<T>(){
public T create(){
final Object obj = f.create();
checkInstanceType(type, obj);
return (T)obj;
}
public String toString(){
return f.toString();
}
};
}
private static abstract class TypedComponent extends DecoratingComponent{
public Class getType(){
return type;
}
public boolean isConcrete(){
return false;
}
//for performance, we suppress type checking for Factory.
public Component factory(String toString){
return getDelegateTarget().factory(toString);
}
public Object create(Dependency dependency){
return checkInstanceType(this.type, super
.create(dependency));
}
public Class verify(Dependency dependency){
super.verify(dependency);
return getType();
}
private final Class type;
TypedComponent(Component cc, Class type) {
super(cc);
this.type = type;
}
}
/**
* Customizes a Component object with a new component type.
* Unlike subsume(), this method allows "down-cast"
* where the current component instance type is a subtype of the given type.
* It also allows "stupid-cast" where the current
* component instance type has no is-a relationship with the target type.
* <p>
* cast forces type checks to be made on this type, regardless of the concrete type.
* </p>
* <p>
* However, when the component's type is concrete, it forwards to subsume.
* </p>
* @param type the target type.
* @return the new Component object.
*/
public static <T> Component<T> cast(final Component<?> cc, final Class<T> type){
final Class suptype = cc.getType();
if(type == suptype) return (Component<T>)cc;
if(type!=null && cc.isConcrete())
return subsume((Component)cc, type);
if(type != null && suptype != null && type.equals(suptype)){
return (Component<T>)cc;
}
return new TypedComponent(cc, type){
protected Component decorate(Component c){
return Components.cast(c,getType());
}
public String toString(){
return "cast <" + cc +"> as " + Misc.getTypeName(getType());
}
}.label();
}
/**
* Customizes a Component object so that the new Component object
* will return a dynamic proxy and hide the real component instance.
* @param cc the Component object to customize.
* @param itfs the interfaces to proxy to.
* @return the new Component object.
*/
public static Component proxy(final Component cc, final Class... itfs){
if(itfs.length==0) //Do nothing for Object.
return cc;
return proxy(cc, itfs, itfs[0].getClassLoader());
}
/**
* Customizes a Component object so that the new Component object
* will return a dynamic proxy and hide the real component instance.
* @param cc the Component object to customize.
* @param itfs the interfaces to proxy to.
* @param loader the ClassLoader object used to create the proxy.
* @return the new Component object.
*/
public static Component proxy(final Component cc,
final Class[] itfs, final ClassLoader loader){
if(itfs.length==0) //Do nothing for Object.
return cc;
final Class type = cc.getType();
if(type!=null)
checkTypes(itfs, type);
//final Class proxyType = InstanceProxy.getProxyClass(cl, itfs);
return new ProxyComponent(cc, itfs, loader).label();
}
/**
* Customizes a Component object so that the new Component object
* will return a dynamic proxy and hide the real component instance.
* @param cc the Component object to customize.
* @param itf the interface to proxy to.
* @return the new Component object.
*/
public static <I> Component<I> proxy(final Component<?> cc, Class<I> itf){
return proxy(cc, toInterfaces(itf));
}
/**
* Customizes a Component object so that the new Component object
* will return a dynamic proxy and hide the real component instance.
* The dynamic proxy will implement all the public interfaces
* implemented by the type of the component being customized.
* @param cc the Component object to customize.
* @return the new Component object.
*/
public static Component proxy(final Component cc){
final Class ctype = cc.getType();
if(ctype == null) return cc;
return proxy(cc, ctype);
}
/**
* Create a Component object that creates an array as instance.
* Upon instance creation, it first invokes the Component objects provided
* one by one and stores the result instances into an array.
* The array is finally returned as the component instance.
* @param ccs the Component objects.
* @param etype the target array element type.
* @return the new Component object.
* @throws IllegalArgumentException if any component in the ccs array
* has an incompatible type with the target array element type.
*/
public static <T> Component<T[]> array(final Component<T>[] ccs,
final Class<T> etype)
throws IllegalArgumentException{
final Class target_type = Misc.getArrayType(etype);
final Component<T[]> step1 = new SimpleComponent<T[]>(target_type){
public T[] create(){
return (T[])Array.newInstance(etype, ccs.length);
}
public String toString(){
return "array "+StringUtils.listArray("[",",","]",ccs)
+ ":"+Misc.getTypeName(target_type);
}
};
return storeArray(step1, ccs);
/*
final Component[] cpy = new Component[ccs.length];
for(int i=0; i<ccs.length; i++){
final Component cc = ccs[i];
final Class ctype = cc.getType();
if(ctype!=null && !ReflectionUtil.isAssignableFrom(etype, ctype)){
throw new IllegalArgumentException("type mismatch: the #"
+i+" element is a component of "+Misc.getTypeName(ctype)
+", while the expected type is "+Misc.getTypeName(etype));
}
cpy[i] = ccs[i];
}
return new ArrayComponent(cpy, target_type).guard().label();*/
}
public static <T> Component<T[]> storeArray(Component<T[]> component_returning_array,
Creator<T>... creators){
final Class type = component_returning_array.getType();
final Class etype = type==null?null:type.getComponentType();
return component_returning_array
.followedBy(new ArrayStoreBinder<T>(creators, etype))
.label();
}
/**
* Create a Component object that creates an array as instance.
* Upon instance creation, it first invokes the Component objects provided
* one by one and stores the result instances into an array.
* The array is finally returned as the component instance.
* <br>
* The element type of the result array is determined by the types
* of the Component objects in the ccs array.
* The algorithm is as following:
* if any type is a common super type of all other component types,
* that type is used as the element type,
* Object.class is used otherwise.
* Note, this algorithm does not attempt to analyze the least common
* super type. So if type 1 is Integer, type 2 is Short,
* Object is used as the element type rather than Number.
*
* @param ccs the Component objects.
* @return the new Component object.
*/
public static <T> Component<T[]> array(final Component<T>... ccs){
return array(ccs, Utils.getCommonRootType(ccs));
/*
return new ArrayComponent(copy(ccs),
Misc.getArrayType(Utils.getCommonRootType(ccs)))
.guard().label();*/
}
/**
* Create a Component object that creates java.util.ArrayList.
* Upon instance creation, it first invokes the Creator objects provided
* one by one and stores the result instances into a java.util.ArrayList object.
* The list is finally returned as the component instance.
*
* @param ccs the Creator objects.
* @return the new Component object.
*/
public static <T> Component<ArrayList<T>> list(final Creator<T>... ccs){
//return new ListComponent(ccs).guard().label();
final Component<ArrayList<T>> step1 = new SimpleComponent<ArrayList<T>>(ArrayList.class){
public ArrayList<T> create(){
return new ArrayList<T>(ccs.length);
}
public String toString(){
return "list "+StringUtils.listArray("[",",","]",ccs);
}
};
return storeList(step1, ccs);
}
/**
* Create a Component object that sequentially execute an array of components
* and collect the result into a java.util.List object.
* @param component_returning_list the Component object that returns a java.util.List.
* @param creators the components to be executed sequentially.
* @return the new Component object.
*/
public static <T,L extends List<T>> Component<L> storeList(final Component<L> component_returning_list,
final Creator<T>... creators){
return component_returning_list.followedBy(new StoreBinder<T,L>(creators){
public ElementStore<T> toStore(L v){
if(v instanceof List){
return new ListStore<T>(v);
}
throw new ClassCastException("java.util.List expected, "
+Utils.getObjTypeName(v)+" encountered");
}
}).label();
}
/**
* Create a Component object that creates java.util.HashSet.
* Upon instance creation, it first invokes the Creator objects provided
* one by one and stores the result instances into a java.util.HashSet object.
* The set is finally returned as the component instance.
*
* @param ccs the Creator objects.
* @return the new Component object.
*/
public static <T> Component<HashSet<T>> hashset(final Creator<T>... ccs){
//return new ListComponent(ccs).guard().label();
final Component<HashSet<T>> step1 = new SimpleComponent<HashSet<T>>(HashSet.class){
public HashSet<T> create(){
return new HashSet<T>(ccs.length);
}
public String toString(){
return "hashset "+StringUtils.listArray("[",",","]",ccs);
}
};
return storeSet(step1, ccs);
}
/**
* Create a Component object that sequentially execute an array of components
* and collect the result into a java.util.Set object.
* @param component_returning_set the Component object that returns a java.util.Set.
* @param creators the components to be executed sequentially.
* @return the new Component object.
*/
public static <T, S extends Set<T>> Component<S> storeSet(final Component<S> component_returning_set,
final Creator<T>... creators){
return component_returning_set.followedBy(new StoreBinder<T,S>(creators){
public ElementStore<T> toStore(S v){
if(v instanceof Set){
return new SetStore<T>(v);
}
throw new ClassCastException("java.util.Set expected, "
+Utils.getObjTypeName(v)+" encountered");
}
}).label();
}
/**
* Create a Component object that creates java.util.ArrayList.
* Upon instance creation, it first invokes the Creator objects provided
* one by one and stores the result instances into a java.util.List object.
* The list is finally returned as the component instance.
*
* @param ccs the Creator objects.
* @return the new Component object.
*/
public static <T, C extends Creator<T>> Component<ArrayList<T>> list(final java.util.List<C> ccs){
final Creator<T>[] crs = new Creator[ccs.size()];
ccs.toArray(crs);
return list(crs);
}
/**
* Create a Component object that creates a java.util.LinkedHashMap object.
* <br>
* Upon instance creation, it first invokes the Creator objects stored
* in the parameter creators one by one.
* The created instances along with
* the corresponding keys stored in the parameter keys
* are saved in a new java.util.LinkedHashMap object.
* This new java.util.LinkedHashMap object is finally returned as the component
* instance of this component.
* <br>
* The instances are populated into the java.util.LinkedHashMap object
* in the same order as keys, so that keys[0] is the first element in the
* java.util.LinkedHashMap object.
* @param keys the keys for each instance.
* @param creators the Creator objects for each instance.
* @return the Component object.
* @throws IllegalArgumentException if keys.length != creators.length
* or any duplicate key is found in the keys array.
*/
public static <K,T> Component<java.util.LinkedHashMap<K, T>> hashmap(final K[] keys, final Creator<T>[] creators)
throws IllegalArgumentException{
//return hashmap(Utils.lhashmap(keys, creators));
if(keys.length != creators.length){
throw new IllegalArgumentException("keys.length=="+keys.length
+ ", vals.length=="+creators.length);
}
final Component<LinkedHashMap<K,T>> step1 = new SimpleComponent<LinkedHashMap<K,T>>(LinkedHashMap.class){
public LinkedHashMap<K,T> create(){
return new LinkedHashMap<K,T>(keys.length);
}
};
return storeMap(step1, keys, creators);
}
/**
* Create a Component that sequentially execute an array of Component object,
* and collect the results in a java.util.Map object.
* @param component_return_map the Component that creates the java.util.Map object.
* @param keys the keys to use.
* @param vals the components to be sequentially executed.
* @return the new Component object.
*/
public static <K,T,M extends java.util.Map<K,T>> Component<M> storeMap(final Component<M> component_return_map,
final K[] keys, Creator<T>[] vals){
return component_return_map.followedBy(new StoreBinder<T,M>(vals){
public ElementStore<T> toStore(M v){
if(v instanceof java.util.Map){
return new MapStore<K,T>(keys, v);
}
else{
throw new ClassCastException("java.util.Map expected, "+
Utils.getObjTypeName(v)+" encountered.");
}
}
}).label();
}
/**
* By default, all components depended by a component
* will be verified when the component is verified.
* incomplete() creates a new Component object that
* suppresses the verification of dependency.
* <br>
* This customization cannot be applied to a late-bound component
* whose getType() returns null.
* In order to suppress verification, first use subsume() or cast()
* to give it a type.
* @param cc the Component to suppress verification for its parameters/properties.
* @return the new Component object.
* @throws UnknownComponentTypeException if the Component is a late-bound
* component whose getType() returns null.
*/
public static <T> Component<T> incomplete(final Component<T> cc)
throws UnknownComponentTypeException{
final Class ctype = cc.getType();
if(ctype ==null){
throw new UnknownComponentTypeException("immature component " + cc +
" cannot be made incomplete");
}
return new ClosureableComponent(cc){
public Class verify(Dependency dependency){
return ctype;
}
protected Component decorate(Component c){
return Components.incomplete(c);
}
public String toString(){
return "incomplete <" + cc + ">";
}
}.label();
}
/**
* Customizes a Component object so that upon creation,
* the new Component object mutates the result component instance before it is returned.
* @param cc the Component object to mutate.
* @param m the Mutation object encapsulating the mutation logic.
* @return the new Component object.
*/
public static <T> Component<T> mutate(final Component<T> cc, final Mutation<T> m){
//return new MutateComponent(cc, m).label();
return cc.followedBy(new Mutation2Binder<T>(m));
}
/**
* Create a Component that repeatedly call another Component for
* certain amount of times.
* @param cc the component to repeated call.
* @param times the number of times to repeat.
* @return the new Component object.
*/
public static <T> Component<T> repeat(final Creator<T> cc, final int times){
if(times<0)
throw new IllegalArgumentException("negative repeat times: "+times);
if(times==0)
return value(null);
if(times==1)
return adapt(cc);
return new RepeatComponent<T>(cc, times).label();
}
/**
* Create a Component that the "create()" method is put in a
* synchronized block to ensure thread safety.
* @param cc the Component object.
* @return the Component object that is "synchronized".
*/
public static <T> Component<T> synchronizedComponent(final Component<T> cc){
return new SynchronizedComponent(cc);
}
static Class[] toInterfaces(Class type){
if(type.isInterface()){
return new Class[]{type};
}
else return type.getInterfaces();
}
private static void checkType(final Class itf, final Class type){
if(!ReflectionUtil.isAssignableFrom(itf, type)){
throw new IllegalArgumentException(
Misc.getTypeName(type) + " is not a subtype of "
+ Misc.getTypeName(itf));
}
}
private static void checkTypes(final Class[] itfs, final Class type){
for(int i=0; i<itfs.length; i++){
final Class itf = itfs[i];
checkType(itf, type);
}
}
/*
private static final class DefaultValue implements java.io.Serializable{
public String toString(){
return "default_value";
}
}
private static final Object default_value = new DefaultValue();
*/
private static Object checkInstanceType(final Class type, final Object r) {
if(type != null && !ReflectionUtil.isInstance(type, r)){
throw new TypeMismatchException(type, r==null?null:r.getClass(),
Misc.getTypeName(type)
+" expected, while " + Misc.getTypeName(Utils.getObjType(r))
+" encountered.");
}
return r;
}
}