package jfun.yan.util;
import java.beans.IntrospectionException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import jfun.util.ArrayAsList;
import jfun.util.Misc;
import jfun.util.beans.BeanType;
import jfun.yan.Binder;
import jfun.yan.Component;
import jfun.yan.ComponentInstantiationException;
import jfun.yan.Components;
import jfun.yan.DefaultingException;
import jfun.yan.Dependency;
import jfun.yan.Functions;
import jfun.yan.Monad;
import jfun.yan.PropertyEntry;
import jfun.yan.YanException;
import jfun.yan.util.resource.ResourceLoader;
/**
* Common utility class that provides some utility functions.
* <p>
* @author Ben Yu
* Nov 14, 2005 9:57:35 PM
*/
public class Utils {
/**
* Convert an array of parameter types to a string in the form of
* "(type1, type2, type3)". Array types are printed in the human readable form.
* @param param_types the parameter types.
* @return the string representation.
*/
public static String toString(Class[] param_types){
if(param_types==null) return "()";
StringBuffer buf = new StringBuffer();
buf.append('(');
for(int i=0; i<param_types.length; i++){
buf.append(Misc.getTypeName(param_types[i]));
if(i<param_types.length-1){
buf.append(", ");
}
}
buf.append(')');
return buf.toString();
}
/**
* To suppress access check against a reflection object.
* SecurityException is silently ignored.
* @param acc the reflection object.
*/
static void forceAccess(AccessibleObject acc){
try{
acc.setAccessible(true);
}
catch(SecurityException e){}
}
/**
* Checkes to see if a component may return a value that's an instance
* of a type.
* @param type the type.
* @param c the component.
* @return true if the component may return a value that's an instance of the type.
*/
public static boolean isCompatible(Class type, jfun.yan.Component c){
final Class ctype = c.getType();
if(ctype==null)
return true;
if(c.isConcrete()){
return ReflectionUtil.isAssignableFrom(type,ctype);
}
else{
return ReflectionUtil.isCompatible(type, ctype);
}
}
/**
* Create a java.util.Set instance.
* @param impltype the actual implementation class.
* If it is null or java.util.Set, java.util.HashSet is used.
* @param capacity the initial capacity.
* @return the Set instance.
* @throws IllegalAccessException when the constructor is not public.
* @throws InstantiationException when the constructor fails.
*/
public static Set createSet(Class impltype, int capacity)
throws IllegalAccessException, InstantiationException{
if(impltype==null || HashSet.class.equals(impltype)
|| Set.class.equals(impltype)){
return new HashSet(capacity);
}
else{
return (Set)createPreallocatedCollection(impltype);
}
}
/**
* Create a java.util.Map instance.
* @param impltype the actual implementation class.
* If it is null or java.util.Map, java.util.HashMap is used.
* @param capacity the initial capacity.
* @return the Map instance.
* @throws IllegalAccessException when the constructor is not public.
* @throws InstantiationException when the constructor fails.
*/
public static Map createMap(Class impltype, int capacity)
throws IllegalAccessException, InstantiationException{
if(impltype==null || HashMap.class.equals(impltype)
|| Map.class.equals(impltype)){
return new HashMap(capacity);
}
else if(Hashtable.class.equals(impltype)){
return new Hashtable(capacity);
}
else{
return (Map)createPreallocatedCollection(impltype);
}
}
/**
* Create a java.util.List instance.
* @param impltype the implementation class.
* If it is null or java.util.List, java.util.ArrayList is used.
* @param capacity the initial capacity.
* @return the List instance.
* @throws IllegalAccessException when the constructor is not public.
* @throws InstantiationException when the constructor fails.
*/
public static List createList(Class impltype, int capacity)
throws IllegalAccessException, InstantiationException{
if(impltype==null || ArrayList.class.equals(impltype)
|| List.class.equals(impltype) || Collection.class.equals(impltype)){
return new ArrayList(capacity);
}
else if(LinkedList.class.equals(impltype)){
return new LinkedList();
}
else if(Vector.class.equals(impltype)){
return new Vector(capacity);
}
return (List)Utils.createPreallocatedCollection(impltype);
}
/**
* To instantiate a collection instance.
* <p>
* The default constructor of the type is called.
* </p>
* @param type the type of the collection.
* @return the instance.
* @throws IllegalAccessException when the constructor is not public.
* @throws InstantiationException when the constructor fails.
*/
public static Object createPreallocatedCollection(Class type)
throws IllegalAccessException, InstantiationException{
return type.newInstance();
}
private static final class HelperMethods{
static final Method binder_bind =
ReflectionUtil.getMethod(Binder.class, "bind", new Class[]{Object.class},
false);
}
/**
* Convert a Binder object's bind() method to a Component that expects
* one parameter and instantiates the component instance by calling
* the return value of the bind() method.
* @param binder the Binder object.
* @return the Component.
*/
public static Component asComponent(Binder binder){
return Components.fun(Functions.method(binder, HelperMethods.binder_bind))
.bind(Monad.instantiator());
}
/**
* Use an array as a list.
* @param arr the array object.
* @return the List object.
*/
public static List asList(Object arr){
if(arr instanceof Object[]){
return Arrays.asList((Object[])arr);
}
else{
return new ArrayAsList(arr);
}
}
/**
* Wrap an exception thrown when instantiating components.
* Error is thrown out directly without being wrapped.
* @param e the exception.
* @return the wrapped exception.
*/
public static YanException wrapInstantiationException(Throwable e){
if(e instanceof Error)
throw (Error)e;
if(e instanceof YanException){
final YanException ye = (YanException)e;
return ye;
}
if(e instanceof InvocationTargetException){
final Throwable cause =
((InvocationTargetException)e).getTargetException();
if(cause != e){
return wrapInstantiationException(cause);
}
}
final YanException ye = new ComponentInstantiationException(e);
return ye;
}
/**
* Inject a property value.
* @param btype the BeanType.
* @param obj the object to inject property into.
* @param name the property name.
* @param dep the dependency to resolve property value.
*/
public static void injectProperty(BeanType btype, Object obj, String name,
Dependency dep){
final Class objtype = obj.getClass();
final Class atype = btype.getPropertySetterType(name);
if(atype!=null){
final Object v = dep.getProperty(objtype, name, atype);
try{
btype.setProperty(obj, name, v);
}
catch(Throwable e){
throw wrapInstantiationException(e);
}
}
}
/**
* Get the BeanType for a class.
* @param type the class.
* @return the BeanType object. Or null if the type is null.
*/
public static BeanType toBeanType(Class type){
if(type==null) return null;
try{
return BeanType.instance(type);
}
catch(IntrospectionException e){
throw new ComponentInstantiationException(e);
}
}
/**
* Inject property values into a bean.
* @param btype the BeanType.
* @param obj the bean object.
* @param props the property names.
* @param dep the dependency object to resolve property values.
*/
public static void injectProperties(BeanType btype, Object obj,
Set props, Dependency dep){
for(Iterator it=props.iterator(); it.hasNext();){
final String name = it.next().toString();
try{
jfun.yan.util.Utils.injectProperty(btype, obj, name, dep);
}
catch(DefaultingException e){
//when default value is used, this property is ignored.
continue;
}
catch(YanException e){
e.push(new PropertyEntry(btype.getType(), name));
throw e;
}
}
}
private static Class getArgumentType(BeanType btype, String name){
return btype.getPropertySetterType(name);
}
/**
* Verify that a set of properties can be resolved.
* @param btype the BeanType object.
* @param props the set of property names.
* @param dep the dependency.
*/
public static void verifyProperties(BeanType btype, Set props, Dependency dep){
for(Iterator it=props.iterator(); it.hasNext();){
final String name = it.next().toString();
final Class atype = getArgumentType(btype, name);
if(atype!=null){
try{
dep.verifyProperty(btype.getType(), name, atype);
}
catch(DefaultingException e){
continue;
}
catch(YanException e){
e.push(new PropertyEntry(btype.getType(), name));
throw e;
}
}
}
}
/**
* Convert an array of objects to a Set. No duplicate is allowed.
* @param keys the array of objects.
* @param item_name the item name in the error message when duplicate is found.
* @return the Set object.
*/
public static java.util.HashSet toSet(Object[] keys, String item_name){
final HashSet hset = new HashSet();
for(int i=0; i<keys.length; i++){
final Object name = keys[i];
if(hset.contains(name)){
throw new IllegalArgumentException("duplicate " + item_name
+": " + name);
}
hset.add(name);
}
return hset;
}
/**
* To read a property file from a class loader
* into a Properties object.
* @param loader the ResourceLoader used to load resource.
* @param resource the resource name.
* @return the Properties object.
* @throws IOException when loading fails.
*/
public static Properties loadResourceProperties(ResourceLoader loader, String resource)
throws IOException{
final Properties props = new Properties();
final InputStream in = loader.getResourceAsStream(resource);
if(in==null){
throw new FileNotFoundException(resource);
}
try{
props.load(in);
}
finally{
try{
in.close();
}
catch(Exception e){}
}
return props;
}
/**
* Get the type name of an object.
* @param arg the object.
* @param nullname the default name if the object is null.
* @return the name.
*/
public static String getObjTypeName(Object arg, String nullname){
return (arg==null)?nullname:Misc.getTypeName(arg.getClass());
}
/**
* Get the type of an object.
* @param arg the object.
* @param nulltype the default type if the object is null.
* @return the type.
*/
public static Class getObjType(Object arg, Class nulltype){
if(arg==null) return nulltype;
else return arg.getClass();
}
}