All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.lib.converter.Converter Maven / Gradle / Ivy

Go to download

This bundle wraps Pi4j (http://pi4j.com) that wraps the native code Wiring Pi (http://wiringpi.com). It wraps these libraries to make them OSGi friendly and allow them to work together with the OSGi enRoute IoT circuit library (osgi.enroute.iot.circuit). The bundle will first use Pi4J to detect on what hardware it runs. If it runs on an appropriate type, it will register a component that can be configured with Metatype. The Metatype defines a full blown configuration template for all the Pi's functions. The GPIO's are registered as components for the circuit. Regardless of the success of the configuration, this bundle will also register a GpioController service, which is the main Pi4J class.

There is a newer version: 2.1.0
Show newest version
package aQute.lib.converter;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;

import aQute.lib.base64.Base64;

/**
 * General Java type converter from an object to any type. Supports number
 * conversion
 */
@SuppressWarnings({
		"unchecked", "rawtypes"
})
public class Converter {
	public interface Hook {
		Object convert(Type dest, Object o) throws Exception;
	}

	boolean			fatal	= true;
	Map	hooks;
	List		allHooks;

	public  T convert(Class type, Object o) throws Exception {
		// Is it a compatible type?
		if (o != null && type.isAssignableFrom(o.getClass()))
			return (T) o;
		return (T) convert((Type) type, o);
	}

	public  T convert(TypeReference type, Object o) throws Exception {
		return (T) convert(type.getType(), o);
	}

	public Object convert(Type type, Object o) throws Exception {
		Class resultType = getRawClass(type);
		if (o == null) {
			if (resultType.isPrimitive() || Number.class.isAssignableFrom(resultType))
				return convert(type, 0);

			return null; // compatible with any
		}

		if (allHooks != null) {
			for (Hook hook : allHooks) {
				Object r = hook.convert(type, o);
				if (r != null)
					return r;
			}
		}

		if (hooks != null) {
			Hook hook = hooks.get(type);
			if (hook != null) {
				Object value = hook.convert(type, o);
				if (value != null)
					return value;
			}
		}

		Class< ? > actualType = o.getClass();

		// We can always make a string

		if (resultType == String.class) {
			if (actualType.isArray()) {
				if (actualType == char[].class)
					return new String((char[]) o);
				if (actualType == byte[].class)
					return Base64.encodeBase64((byte[]) o);
				int l = Array.getLength(o);
				StringBuilder sb = new StringBuilder("[");
				String del = "";
				for (int i = 0; i < l; i++) {
					sb.append(del);
					del = ",";
					sb.append(convert(String.class, Array.get(o, i)));
				}
				sb.append("]");
				return sb.toString();
			}
			return o.toString();
		}

		// or make a UUID
		if (resultType == UUID.class) {
			return UUID.fromString(o.toString());
		}

		//
		// In case we have a Dictionary that is not also a map
		// this is kind of opportune in OSGi because of the silly
		// dictionaries we're still having
		//
		
		if ( o instanceof Dictionary && !(o instanceof Map)) {
			Dictionary dict = (Dictionary< ? , ? >) o;
			Map map = new HashMap();
			Enumeration e = dict.keys();
			while (e.hasMoreElements()) {
				Object k = e.nextElement();
				Object v = dict.get(k);
				map.put(k, v);
			}
			o = map;
		}
		
		if (Collection.class.isAssignableFrom(resultType))
			return collection(type, resultType, o);


		if (Map.class.isAssignableFrom(resultType))
			return map(type, resultType, o);

		
		if (type instanceof GenericArrayType) {
			GenericArrayType gType = (GenericArrayType) type;
			return array(gType.getGenericComponentType(), o);
		}

		if (resultType.isArray()) {
			if (actualType == String.class) {
				String s = (String) o;
				if (byte[].class == resultType)
					return Base64.decodeBase64(s);

				if (char[].class == resultType)
					return s.toCharArray();
			}
			if (byte[].class == resultType) {
				// Sometimes classes implement toByteArray
				try {
					Method m = actualType.getMethod("toByteArray");
					if (m.getReturnType() == byte[].class)
						return m.invoke(o);

				}
				catch (Exception e) {
					// Ignore
				}
			}

			return array(resultType.getComponentType(), o);
		}

		if (resultType.isAssignableFrom(o.getClass()))
			return o;

		if (Map.class.isAssignableFrom(actualType) && resultType.isInterface()) {
			return proxy(resultType, (Map) o);
		}

		// Simple type coercion

		if (resultType == boolean.class || resultType == Boolean.class) {
			if (actualType == boolean.class || actualType == Boolean.class)
				return o;
			Number n = number(o);
			if (n != null)
				return n.longValue() == 0 ? false : true;

			resultType = Boolean.class;
		} else if (resultType == byte.class || resultType == Byte.class) {
			Number n = number(o);
			if (n != null)
				return n.byteValue();
			resultType = Byte.class;
		} else if (resultType == char.class || resultType == Character.class) {
			Number n = number(o);
			if (n != null)
				return (char) n.shortValue();
			resultType = Character.class;
		} else if (resultType == short.class || resultType == Short.class) {
			Number n = number(o);
			if (n != null)
				return n.shortValue();

			resultType = Short.class;
		} else if (resultType == int.class || resultType == Integer.class) {
			Number n = number(o);
			if (n != null)
				return n.intValue();

			resultType = Integer.class;
		} else if (resultType == long.class || resultType == Long.class) {
			Number n = number(o);
			if (n != null)
				return n.longValue();

			resultType = Long.class;
		} else if (resultType == float.class || resultType == Float.class) {
			Number n = number(o);
			if (n != null)
				return n.floatValue();

			resultType = Float.class;
		} else if (resultType == double.class || resultType == Double.class) {
			Number n = number(o);
			if (n != null)
				return n.doubleValue();

			resultType = Double.class;
		}

		assert !resultType.isPrimitive();

		if (actualType == String.class) {
			String input = (String) o;
			if (resultType == char[].class)
				return input.toCharArray();

			if (resultType == byte[].class)
				return Base64.decodeBase64(input);

			if (Enum.class.isAssignableFrom(resultType)) {
				try {
					return Enum.valueOf((Class) resultType, input);
				} catch( Exception e) {
					input = input.toUpperCase();
					return Enum.valueOf((Class) resultType, input);
				}
			}
			if (resultType == Pattern.class) {
				return Pattern.compile(input);
			}
			if (resultType == URI.class) {
				return new URI(sanitizeInputForURI(input));
			}

			try {
				Constructor< ? > c = resultType.getConstructor(String.class);
				return c.newInstance(o.toString());
			}
			catch (Throwable t) {}
			try {
				Method m = resultType.getMethod("valueOf", String.class);
				if (Modifier.isStatic(m.getModifiers()))
					return m.invoke(null, o.toString());
			}
			catch (Throwable t) {}

			if (resultType == Character.class && input.length() == 1)
				return input.charAt(0);
		}
		Number n = number(o);
		if (n != null) {
			if (Enum.class.isAssignableFrom(resultType)) {
				try {
					Method values = resultType.getMethod("values");
					Enum[] vs = (Enum[]) values.invoke(null);
					int nn = n.intValue();
					if (nn > 0 && nn < vs.length)
						return vs[nn];
				}
				catch (Exception e) {
					// Ignore
				}
			}
		}

		// Translate arrays with length 1 by picking the single element
		if (actualType.isArray() && Array.getLength(o) == 1) {
			return convert(type, Array.get(o, 0));
		}

		// Translate collections with size 1 by picking the single element
		if (o instanceof Collection) {
			Collection col = (Collection) o;
			if (col.size() == 1)
				return convert(type, col.iterator().next());
		}

		if (o instanceof Map) {
			String key = null;
			try {
				Map map = (Map) o;
				Object instance = resultType.newInstance();
				for (Map.Entry e : map.entrySet()) {
					key = (String) e.getKey();
					try {
						Field f = resultType.getField(key);
						Object value = convert(f.getGenericType(), e.getValue());
						f.set(instance, value);
					}
					catch (Exception ee) {

						// We cannot find the key, so try the __extra field
						Field f = resultType.getField("__extra");
						Map extra = (Map) f.get(instance);
						if (extra == null) {
							extra = new HashMap();
							f.set(instance, extra);
						}
						extra.put(key, convert(Object.class, e.getValue()));

					}
				}
				return instance;
			}
			catch (Exception e) {
				return error("No conversion found for " + o.getClass() + " to " + type + ", error " + e + " on key "
						+ key);
			}
		}

		return error("No conversion found for " + o.getClass() + " to " + type);
	}

	private String sanitizeInputForURI(String input) {
		int newline = input.indexOf("\n");
		if (newline > -1)
			return input.substring(0, newline).trim();
		return input;
	}

	private Number number(Object o) {
		if (o instanceof Number)
			return (Number) o;

		if (o instanceof Boolean)
			return ((Boolean) o).booleanValue() ? 1 : 0;

		if (o instanceof Character)
			return (int) ((Character) o).charValue();

		if (o instanceof String) {
			String s = (String) o;
			try {
				return Double.parseDouble(s);
			}
			catch (Exception e) {
				// Ignore
			}
		}
		return null;
	}

	private Collection collection(Type collectionType, Class< ? extends Collection> rawClass, Object o)
			throws Exception {
		Collection collection;
		if (rawClass.isInterface() || Modifier.isAbstract(rawClass.getModifiers())) {
			if (rawClass.isAssignableFrom(ArrayList.class))
				collection = new ArrayList();
			else if (rawClass.isAssignableFrom(HashSet.class))
				collection = new HashSet();
			else if (rawClass.isAssignableFrom(TreeSet.class))
				collection = new TreeSet();
			else if (rawClass.isAssignableFrom(LinkedList.class))
				collection = new LinkedList();
			else if (rawClass.isAssignableFrom(Vector.class))
				collection = new Vector();
			else if (rawClass.isAssignableFrom(Stack.class))
				collection = new Stack();
			else if (rawClass.isAssignableFrom(ConcurrentLinkedQueue.class))
				collection = new ConcurrentLinkedQueue();
			else
				return (Collection) error("Cannot find a suitable collection for the collection interface " + rawClass);
		} else
			collection = rawClass.newInstance();

		Type subType = Object.class;
		if (collectionType instanceof ParameterizedType) {
			ParameterizedType ptype = (ParameterizedType) collectionType;
			subType = ptype.getActualTypeArguments()[0];
		}

		Collection input = toCollection(o);

		for (Object i : input)
			collection.add(convert(subType, i));

		return collection;
	}

	private Map map(Type mapType, Class< ? extends Map< ? , ? >> rawClass, Object o) throws Exception {
		Map result;
		if (rawClass.isInterface() || Modifier.isAbstract(rawClass.getModifiers())) {
			if (rawClass.isAssignableFrom(HashMap.class))
				result = new HashMap();
			else if (rawClass.isAssignableFrom(TreeMap.class))
				result = new TreeMap();
			else if (rawClass.isAssignableFrom(ConcurrentHashMap.class))
				result = new ConcurrentHashMap();
			else {
				return (Map) error("Cannot find suitable map for map interface " + rawClass);
			}
		} else
			result = rawClass.newInstance();

		Map< ? , ? > input = toMap(o);

		Type keyType = Object.class;
		Type valueType = Object.class;
		if (mapType instanceof ParameterizedType) {
			ParameterizedType ptype = (ParameterizedType) mapType;
			keyType = ptype.getActualTypeArguments()[0];
			valueType = ptype.getActualTypeArguments()[1];
		}

		for (Map.Entry< ? , ? > entry : input.entrySet()) {
			Object key = convert(keyType, entry.getKey());
			Object value = convert(valueType, entry.getValue());
			if (key == null)
				error("Key for map must not be null: " + input);
			else
				result.put(key, value);
		}

		return result;
	}

	public Object array(Type type, Object o) throws Exception {
		Collection< ? > input = toCollection(o);
		Class< ? > componentClass = getRawClass(type);
		Object array = Array.newInstance(componentClass, input.size());

		int i = 0;
		for (Object next : input) {
			Array.set(array, i++, convert(type, next));
		}
		return array;
	}

	private Class< ? > getRawClass(Type type) {
		if (type instanceof Class)
			return (Class< ? >) type;

		if (type instanceof ParameterizedType)
			return (Class< ? >) ((ParameterizedType) type).getRawType();

		if (type instanceof GenericArrayType) {
			Type componentType = ((GenericArrayType) type).getGenericComponentType();
			return Array.newInstance(getRawClass(componentType), 0).getClass();
		}

		if (type instanceof TypeVariable) {
			Type componentType = ((TypeVariable) type).getBounds()[0];
			return Array.newInstance(getRawClass(componentType), 0).getClass();
		}

		if (type instanceof WildcardType) {
			Type componentType = ((WildcardType) type).getUpperBounds()[0];
			return Array.newInstance(getRawClass(componentType), 0).getClass();
		}

		return Object.class;
	}

	public Collection< ? > toCollection(Object o) {
		if (o instanceof Collection)
			return (Collection< ? >) o;

		if (o.getClass().isArray()) {
			if (o.getClass().getComponentType().isPrimitive()) {
				int length = Array.getLength(o);
				List result = new ArrayList(length);
				for (int i = 0; i < length; i++) {
					result.add(Array.get(o, i));
				}
				return result;
			}
			return Arrays.asList((Object[]) o);
		}

		return Arrays.asList(o);
	}

	public Map< ? , ? > toMap(Object o) throws Exception {
		if (o instanceof Map)
			return (Map< ? , ? >) o;
		Map result = new HashMap();
		Field fields[] = o.getClass().getFields();
		for (Field f : fields)
			result.put(f.getName(), f.get(o));
		if (result.isEmpty())
			return null;

		return result;
	}

	private Object error(String string) {
		if (fatal)
			throw new IllegalArgumentException(string);
		return null;
	}

	public void setFatalIsException(boolean b) {
		fatal = b;
	}

	public Converter hook(Type type, Hook hook) {
		if (type != null) {
			if (hooks == null)
				hooks = new HashMap();
			this.hooks.put(type, hook);
		} else {
			if (allHooks == null)
				allHooks = new ArrayList();
			allHooks.add(hook);
		}

		return this;
	}

	/**
	 * Convert a map to an interface.
	 * 
	 * @param interfc
	 * @param properties
	 * @return
	 */
	public  T proxy(Class interfc, final Map< ? , ? > properties) {
		return (T) Proxy.newProxyInstance(interfc.getClassLoader(), new Class[] {
			interfc
		}, new InvocationHandler() {

			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object o = properties.get(method.getName());
				if (o == null)
					o = properties.get(mangleMethodName(method.getName()));


				if ( o == null) {
					if ( args!=null && args.length == 1 ) {
						o = args[0];
					} else {
						o = method.getDefaultValue();
					}
				}

				return convert(method.getGenericReturnType(), o);
			}

		});
	}

	public static String mangleMethodName(String id) {
		StringBuilder sb = new StringBuilder(id);
		for (int i = 0; i < sb.length(); i++) {
			char c = sb.charAt(i);
			boolean twice = i < sb.length() - 1 && sb.charAt(i + 1) == c;
			if (c == '$' || c == '_') {
				if (twice)
					sb.deleteCharAt(i + 1);
				else if (c == '$')
					sb.deleteCharAt(i--); // Remove dollars
				else
					sb.setCharAt(i, '.'); // Make _ into .
			}
		}
		return sb.toString();
	}

	public static  T cnv(TypeReference tr, Object source) throws Exception {
		return new Converter().convert(tr, source);
	}

	public static  T cnv(Class tr, Object source) throws Exception {
		return new Converter().convert(tr, source);
	}

	public static Object cnv(Type tr, Object source) throws Exception {
		return new Converter().convert(tr, source);
	}

}