edu.washington.cs.cupid.TypeManager.java Source code

Java tutorial

Introduction

Here is the source code for edu.washington.cs.cupid.TypeManager.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Todd Schiller.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Todd Schiller - initial API, implementation, and documentation
 ******************************************************************************/
package edu.washington.cs.cupid;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;

import edu.washington.cs.cupid.capability.CapabilityUtil;
import edu.washington.cs.cupid.capability.ICapability;
import edu.washington.cs.cupid.capability.Output;
import edu.washington.cs.cupid.capability.ICapability.IOutput;
import edu.washington.cs.cupid.internal.TypeAdapterRegistry;
import edu.washington.cs.cupid.types.ITypeAdapter;
import edu.washington.cs.cupid.types.ITypeAdapterRegistry;

/**
 * Manages the Cupid type hierarchy.
 * @author Todd Schiller
 */
public final class TypeManager {

    private static final ITypeAdapterRegistry ADAPTER_REGISTRY = new TypeAdapterRegistry();

    private TypeManager() {
        // NO OP
    }

    private static final Map<Class<?>, Class<?>> BOXED_TYPES = ImmutableMap.<Class<?>, Class<?>>builder()
            .put(boolean.class, Boolean.class).put(byte.class, Byte.class).put(char.class, Character.class)
            .put(short.class, Short.class).put(int.class, Integer.class).put(long.class, Long.class)
            .put(float.class, Float.class).put(double.class, Double.class).put(void.class, Void.class)
            .put(boolean[].class, Boolean[].class).put(byte[].class, Byte[].class)
            .put(char[].class, Character[].class).put(short[].class, Short[].class)
            .put(int[].class, Integer[].class).put(long[].class, Long[].class).put(float[].class, Float[].class)
            .put(double[].class, Double[].class).build();

    /**
     * Returns the Cupid type adapter registry.
     * @return the Cupid type adapter registry
     */
    public static ITypeAdapterRegistry getTypeAdapterRegistry() {
        return ADAPTER_REGISTRY;
    }

    private static Set<Class<?>> getClassesBfs(Class<?> clazz) {
        // adapted from: http://stackoverflow.com/questions/9797212/finding-the-nearest-common-superclass-or-superinterface-of-a-collection-of-cla

        Set<Class<?>> classes = Sets.newLinkedHashSet();
        Set<Class<?>> nextLevel = Sets.newLinkedHashSet();
        nextLevel.add(clazz);
        do {
            classes.addAll(nextLevel);
            Set<Class<?>> thisLevel = Sets.newLinkedHashSet(nextLevel);
            nextLevel.clear();
            for (Class<?> each : thisLevel) {
                Class<?> superClass = each.getSuperclass();
                if (superClass != null && superClass != Object.class) {
                    nextLevel.add(superClass);
                }
                for (Class<?> eachInt : each.getInterfaces()) {
                    nextLevel.add(eachInt);
                }
            }
        } while (!nextLevel.isEmpty());
        return classes;
    }

    /**
     * Returns the common set of superclasses sorted by distance from the first class?
     * @param classes
     * @return
     */
    public static List<Class<?>> commonSuperClass(Class<?>... classes) {
        // adapted from: http://stackoverflow.com/questions/9797212/finding-the-nearest-common-superclass-or-superinterface-of-a-collection-of-cla

        Set<Class<?>> result = new LinkedHashSet<Class<?>>(getClassesBfs(classes[0]));
        for (Class<?> clazz : classes) {
            result.retainAll(getClassesBfs(clazz));
        }
        return Lists.newArrayList(result);
    }

    private static boolean isPrimitive(Type type) {
        return type instanceof Class && ((Class<?>) type).isPrimitive();
    }

    /**
     * Return the associated boxed or boxed array type for <code>type</code>. Returns <code>type</code>, if <code>type</code> is
     * not a primitive or primitive array type.
     * @param type the type token
     * @return the associated boxed or boxed array type
     */
    public static TypeToken<?> boxType(TypeToken<?> type) {
        if (isPrimitive(type.getType())) {
            return TypeToken.of(BOXED_TYPES.get((Class<?>) type.getType()));
        } else {
            return type;
        }
    }

    public static boolean isBoxedType(TypeToken<?> type) {
        return BOXED_TYPES.values().contains(type.getRawType());
    }

    /**
     * Returns <code>true</code> iff <code>lhs</code> can be assigned to <code>rhs</code> according to
     * Java's standard typing rules. Uses {@link TypeToken#isAssignableFrom(TypeToken)}.
     * @param lhs the left-hand side 
     * @param rhs the right-hand side
     * @return <code>true</code> iff <code>lhs</code> can be assigned to <code>rhs</code> modulo
     * Java's standard typing.
     */
    public static boolean isJavaCompatible(final TypeToken<?> lhs, final TypeToken<?> rhs) {
        return lhs.isAssignableFrom(rhs);
    }

    public static String simpleTypeName(final TypeToken<?> type) {
        return simpleTypeName(type.getType());
    }

    /**
     * Returns a human-readable name for <code>type</code>.
     * @param type the type
     * @return a human-readable name for <code>type</code>.
     */
    public static String simpleTypeName(final Type type) {
        StringBuilder result = new StringBuilder();

        if (type instanceof Class) {
            result.append(((Class<?>) type).getSimpleName());
        } else if (type instanceof ParameterizedType) {
            List<String> params = Lists.newArrayList();

            for (Type param : ((ParameterizedType) type).getActualTypeArguments()) {
                params.add(simpleTypeName(param));
            }

            result.append(simpleTypeName(((ParameterizedType) type).getRawType())).append("<")
                    .append(Joiner.on(", ").join(params)).append(">");
        } else if (type instanceof GenericArrayType) {
            result.append(simpleTypeName(((GenericArrayType) type).getGenericComponentType())).append("[]");
        } else if (type instanceof TypeVariable) {
            result.append(((TypeVariable<?>) type).getName());
        }

        return result.toString();
    }

    /**
     * Returns <code>true</code> iff argument <code>argument</code> can be supplied as the argument
     * for <code>parameter</code>.
     * 
     * @param capability the parameter
     * @param argument the argument
     * @return <code>true</code> iff argument <code>argument</code> can be supplied as the argument
     * for <code>capability</code>
     */
    public static boolean isCompatible(final ICapability.IParameter<?> parameter, final Object argument) {
        return isCompatible(parameter, TypeToken.of(argument.getClass()));
    }

    /**
     * Returns <code>true</code> iff an argument of type <code>argumentType</code> can be supplied as the argument
     * for <code>capability</code>.
     * 
     * @param capability the capability
     * @param argumentType the argument type
     * @return <code>true</code> iff an argument of type <code>argumentType</code> can be supplied as the argument
     * for <code>capability</code>
     */
    public static boolean isCompatible(final ICapability.IParameter<?> parameter, final TypeToken<?> argumentType) {

        TypeToken<?> parameterType = parameter.getType();

        if (parameterType.equals(TypeToken.of(Void.class))) {
            // parameter does not expect any input
            return true;

        } else if (isJavaCompatible(parameterType, argumentType)) {
            // Java's standard typing rules work
            return true;
        } else if (parameterType.getType() instanceof ParameterizedType) {
            if (parameterType.getRawType().isAssignableFrom(argumentType.getRawType())) {
                // check if type is all variables (i.e., fully generic)
                for (Type arg : ((ParameterizedType) parameterType.getType()).getActualTypeArguments()) {
                    if (!(arg instanceof TypeVariable)) {
                        return parameterType.isAssignableFrom(argumentType);
                    }
                }
                return true;
            } else {
                return false;
            }
        } else {
            return ADAPTER_REGISTRY.getTypeAdapter(argumentType, parameterType) != null;
        }
    }

    /**
     * @param capability the capability
     * @param argument the suggested argument
     * @return <code>argument</code> iff it is compatible with <code>capability</code>; 
     * a corresponding compatible argument, otherwise
     * @see {@link TypeManager#isCompatible(ICapability, Object)}
     */
    public static Object getCompatible(final ICapability.IParameter<?> parameter, final Object argument) {
        if (parameter.getType().equals(TypeToken.of(Void.class))) {
            return argument;
        } else if (isJavaCompatible(parameter.getType(), TypeToken.of(argument.getClass()))) {
            return argument;
        } else {
            @SuppressWarnings("rawtypes")
            ITypeAdapter adapter = ADAPTER_REGISTRY.getTypeAdapter(TypeToken.of(argument.getClass()),
                    parameter.getType());

            if (adapter == null) {
                throw new IllegalArgumentException("Argument is not compatible with capability");
            } else {
                return adapter.adapt(argument);
            }
        }
    }

    public static TypeToken<?> forName(String qualifiedName) throws ClassNotFoundException {
        return TypeToken.of(Class.forName(qualifiedName));
    }
}