# Classes.java

    /*
     * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    package org.qi4j.api.util;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.AccessibleObject;
    import java.lang.reflect.Array;
    import java.lang.reflect.Field;
    import java.lang.reflect.GenericArrayType;
    import java.lang.reflect.Member;
    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.lang.reflect.TypeVariable;
    import java.lang.reflect.WildcardType;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import org.qi4j.api.composite.ModelDescriptor;
    import org.qi4j.functional.Function;
    import org.qi4j.functional.Iterables;
    import org.qi4j.functional.Specification;

    import static org.qi4j.functional.Iterables.cast;
    import static org.qi4j.functional.Iterables.empty;
    import static org.qi4j.functional.Iterables.flatten;
    import static org.qi4j.functional.Iterables.flattenIterables;
    import static org.qi4j.functional.Iterables.iterable;
    import static org.qi4j.functional.Iterables.map;
    import static org.qi4j.functional.Iterables.matchesAny;
    import static org.qi4j.functional.Iterables.prepend;

    /**
     * Useful methods for handling Classes.
     */
    public final class Classes
    {
        private final static Map<Type, Type> wrapperClasses = new HashMap<>();

        static
        {
            wrapperClasses.put( boolean.class, Boolean.class );
            wrapperClasses.put( byte.class, Byte.class );
            wrapperClasses.put( short.class, Short.class );
            wrapperClasses.put( char.class, Character.class );
            wrapperClasses.put( int.class, Integer.class );
            wrapperClasses.put( long.class, Long.class );
            wrapperClasses.put( float.class, Float.class );
            wrapperClasses.put( double.class, Double.class );
        }

        private final static Map<Type, Type> primitiveClasses = new HashMap<>();

        static
        {
            primitiveClasses.put( boolean.class, Boolean.class );
            primitiveClasses.put( byte.class, Byte.class );
            primitiveClasses.put( short.class, Short.class );
            primitiveClasses.put( char.class, Character.class );
            primitiveClasses.put( int.class, Integer.class );
            primitiveClasses.put( long.class, Long.class );
            primitiveClasses.put( float.class, Float.class );
            primitiveClasses.put( double.class, Double.class );
        }

        /**
         * Convert from primitive class (int, short, double, etc.) to wrapper class (Integer, Short, Double, etc.).
         * Return the same class if it's not a primitive class. This can therefore safely be used on all types
         * to ensure that they are not primitives.
         */
        private static final Function<Type, Type> WRAPPER_CLASS = new Function<Type, Type>()
        {
            @Override
            public Type map( Type aClass )
            {
                Type wrapperClass = wrapperClasses.get( aClass );
                return wrapperClass == null ? aClass : wrapperClass;
            }
        };

        /**
         * Convert from wrapper class (Integer, Short, Double, etc.) to primitive class (int, short, double, etc.).
         * Return the same class if it's not a wrapper class. This can therefore safely be used on all types
         * to ensure that they are primitives if possible.
         */
        @SuppressWarnings( "UnusedDeclaration" )
        private static final Function<Type, Type> PRIMITIVE_CLASS = new Function<Type, Type>()
        {
            @Override
            public Type map( Type aClass )
            {
                Type primitiveClass = primitiveClasses.get( aClass );
                return primitiveClass == null ? aClass : primitiveClass;
            }
        };

        /**
         * Function that extract the raw class of a type.
         */
        public static final Function<Type, Class<?>> RAW_CLASS = new Function<Type, Class<?>>()
        {
            @Override
            public Class<?> map( Type genericType )
            {
                // Calculate raw type
                if( genericType instanceof Class )
                {
                    return (Class<?>) genericType;
                }
                else if( genericType instanceof ParameterizedType )
                {
                    return (Class<?>) ( (ParameterizedType) genericType ).getRawType();
                }
                else if( genericType instanceof TypeVariable )
                {
                    return (Class<?>) ( (TypeVariable) genericType ).getGenericDeclaration();
                }
                else if( genericType instanceof WildcardType )
                {
                    return (Class<?>) ( (WildcardType) genericType ).getUpperBounds()[ 0];
                }
                else if( genericType instanceof GenericArrayType )
                {
                    Object temp = Array.newInstance( (Class<?>) ( (GenericArrayType) genericType ).getGenericComponentType(), 0 );
                    return temp.getClass();
                }
                throw new IllegalArgumentException( "Could not extract the raw class of " + genericType );
            }
        };

        private static final Function<AccessibleObject, Type> TYPE_OF = new Function<AccessibleObject, Type>()
        {
            @Override
            public Type map( AccessibleObject accessor )
            {
                return accessor instanceof Method ? ( (Method) accessor ).getGenericReturnType() : ( (Field) accessor ).getGenericType();
            }
        };

        private static final Function<Type, Iterable<Class<?>>> CLASS_HIERARCHY = new Function<Type, Iterable<Class<?>>>()
        {
            @Override
            @SuppressWarnings( {"raw", "unchecked"} )
            public Iterable<Class<?>> map( Type type )
            {
                if( type == null )
                {
                    return empty();
                }
                if( type.equals( Object.class ) )
                {
                    Class<?> aClass = (Class<?>) type;
                    return cast( iterable( aClass ) );
                }
                else
                {
                    type = RAW_CLASS.map( type );
                    Class superclass = ( (Class) type ).getSuperclass();
                    return prepend( (Class<?>) type, map( superclass ) );
                }
            }
        };

        @SuppressWarnings( "raw" )
        private static final Function<Type, Iterable<Type>> INTERFACES_OF = new Function<Type, Iterable<Type>>()
        {
            @Override
            public Iterable<Type> map( Type type )
            {
                Class clazz = RAW_CLASS.map( type );

                if( clazz.isInterface() )
                {
                    Iterable<Type> genericInterfaces = iterable( clazz.getGenericInterfaces() );
                    Iterable<Type> flattenIterables = flattenIterables( Iterables.map( INTERFACES_OF, genericInterfaces ) );
                    return prepend( type, flattenIterables );
                }
                else
                {
                    if( type.equals( Object.class ) )
                    {
                        return iterable( clazz.getGenericInterfaces() );
                    }
                    else
                    {
                        return flatten( flattenIterables( Iterables.map( INTERFACES_OF,
                                                                         iterable( clazz.getGenericInterfaces() ) ) ),
                                        INTERFACES_OF.map( RAW_CLASS.map( type ).getSuperclass() ) );
                    }
                }
            }
        };

        @SuppressWarnings( "raw" )
        private static final Function<Type, Iterable<Type>> TYPES_OF = new Function<Type, Iterable<Type>>()
        {
            @Override
            public Iterable<Type> map( Type type )
            {
                Class clazz = RAW_CLASS.map( type );

                if( clazz.isInterface() )
                {
                    Iterable<Type> genericInterfaces = iterable( clazz.getGenericInterfaces() );
                    Iterable<Type> flattenIterables = flattenIterables( Iterables.map( INTERFACES_OF, genericInterfaces ) );
                    return prepend( clazz, flattenIterables );
                }
                else
                {
                    return flatten( CLASS_HIERARCHY.map( type ),
                                    flattenIterables( Iterables.map( INTERFACES_OF, CLASS_HIERARCHY.map( type ) ) ) );
                }
            }
        };

        public static Type typeOf( AccessibleObject from )
        {
            return TYPE_OF.map( from );
        }

        public static Iterable<Type> typesOf( Iterable<Type> types )
        {
            Iterable<Type> result = empty();
            for( Type type : types )
            {
                result = flatten( result, typesOf( type ) );
            }
            return result;
        }

        public static Iterable<Type> typesOf( Type type )
        {
            return TYPES_OF.map( type );
        }

        public static Iterable<? extends Type> interfacesOf( Iterable<? extends Type> types )
        {
            Iterable<Type> result = empty();
            for( Type type : types )
            {
                result = flatten( result, interfacesOf( type ) );
            }
            return result;
        }

        public static Iterable<Type> interfacesOf( Type type )
        {
            return INTERFACES_OF.map( type );
        }

        public static Iterable<Class<?>> classHierarchy( Class<?> type )
        {
            return CLASS_HIERARCHY.map( type );
        }

        public static Type wrapperClass( Type type )
        {
            return WRAPPER_CLASS.map( type );
        }

        public static Specification<Class<?>> isAssignableFrom( final Class clazz )
        {
            return new Specification<Class<?>>()
            {
                @Override
                @SuppressWarnings( "unchecked" )
                public boolean satisfiedBy( Class<?> item )
                {
                    return clazz.isAssignableFrom( item );
                }
            };
        }

        @SuppressWarnings( "raw" )
        public static Specification<Object> instanceOf( final Class clazz )
        {
            return new Specification<Object>()
            {
                @Override
                public boolean satisfiedBy( Object item )
                {
                    return clazz.isInstance( item );
                }
            };
        }

        public static Specification<Class<?>> hasModifier( final int classModifier )
        {
            return new Specification<Class<?>>()
            {
                @Override
                public boolean satisfiedBy( Class<?> item )
                {
                    return ( item.getModifiers() & classModifier ) != 0;
                }
            };
        }

        public static <T> Function<Type, Iterable<T>> forClassHierarchy( final Function<Class<?>, Iterable<T>> function )
        {
            return new Function<Type, Iterable<T>>()
            {
                @Override
                public Iterable<T> map( Type type )
                {
                    return flattenIterables( Iterables.map( function, CLASS_HIERARCHY.map( type ) ) );
                }
            };
        }

        public static <T> Function<Type, Iterable<T>> forTypes( final Function<Type, Iterable<T>> function )
        {
            return new Function<Type, Iterable<T>>()
            {
                @Override
                public Iterable<T> map( Type type )
                {
                    return flattenIterables( Iterables.map( function, TYPES_OF.map( type ) ) );
                }
            };
        }

        @SuppressWarnings( "raw" )
        public static Set<Class<?>> interfacesWithMethods( Set<Class<?>> interfaces )
        {
            Set<Class<?>> newSet = new LinkedHashSet<>();
            for( Class type : interfaces )
            {
                if( type.isInterface() && type.getDeclaredMethods().length > 0 )
                {
                    newSet.add( type );
                }
            }

            return newSet;
        }

        public static String simpleGenericNameOf( Type type )
        {
            StringBuilder sb = new StringBuilder();
            simpleGenericNameOf( sb, type );
            return sb.toString();
        }

        @SuppressWarnings( "raw" )
        private static void simpleGenericNameOf( StringBuilder sb, Type type )
        {
            if( type instanceof Class )
            {
                sb.append( ( (Class) type ).getSimpleName() );
            }
            else if( type instanceof ParameterizedType )
            {
                ParameterizedType pt = (ParameterizedType) type;
                simpleGenericNameOf( sb, pt.getRawType() );
                sb.append( "<" );
                boolean atLeastOne = false;
                for( Type typeArgument : pt.getActualTypeArguments() )
                {
                    if( atLeastOne )
                    {
                        sb.append( ", " );
                    }
                    simpleGenericNameOf( sb, typeArgument );
                    atLeastOne = true;
                }
                sb.append( ">" );
            }
            else if( type instanceof GenericArrayType )
            {
                GenericArrayType gat = (GenericArrayType) type;
                simpleGenericNameOf( sb, gat.getGenericComponentType() );
                sb.append( "[]" );
            }
            else if( type instanceof TypeVariable )
            {
                TypeVariable tv = (TypeVariable) type;
                sb.append( tv.getName() );
            }
            else if( type instanceof WildcardType )
            {
                WildcardType wt = (WildcardType) type;
                sb.append( "? extends " );
                boolean atLeastOne = false;
                for( Type typeArgument : wt.getUpperBounds() )
                {
                    if( atLeastOne )
                    {
                        sb.append( ", " );
                    }
                    simpleGenericNameOf( sb, typeArgument );
                    atLeastOne = true;
                }
            }
            else
            {
                throw new IllegalArgumentException( "Don't know how to deal with type:" + type );
            }
        }

        @SuppressWarnings( "UnusedDeclaration" )
        public static <AnnotationType extends Annotation>
            AnnotationType findAnnotationOfTypeOrAnyOfSuperTypes( Class<?> type, Class<AnnotationType> annotationClass )
        {
            AnnotationType result = null;
            for( Type clazz : Classes.TYPES_OF.map( type ) )
            {
                result = Classes.RAW_CLASS.map( clazz ).getAnnotation( annotationClass );
                if( result != null )
                {
                    break;
                }
            }

            return result;
        }

        public static Specification<Member> memberNamed( final String name )
        {
            return new Specification<Member>()
            {
                @Override
                public boolean satisfiedBy( Member item )
                {
                    return item.getName().equals( name );
                }
            };
        }

        /**
         * Given a type variable, find what it resolves to given the declaring class where type
         * variable was found and a top class that extends the declaring class.
         *
         * @param name The TypeVariable name.
         * @param declaringClass The class where the TypeVariable is declared.
         * @param topClass The top class that extends the declaringClass
         *
         * @return The Type instance of the given TypeVariable
         */
        @SuppressWarnings( "raw" )
        public static Type resolveTypeVariable( TypeVariable name, Class declaringClass, Class topClass )
        {
            Type type = resolveTypeVariable( name, declaringClass, new HashMap<TypeVariable, Type>(), topClass );
            if( type == null )
            {
                type = Object.class;
            }
            return type;
        }

        @SuppressWarnings( "raw" )
        private static Type resolveTypeVariable( TypeVariable name,
                                                 Class declaringClass,
                                                 Map<TypeVariable, Type> mappings,
                                                 Class current
        )
        {
            if( current.equals( declaringClass ) )
            {
                Type resolvedType = name;
                while( resolvedType instanceof TypeVariable )
                {
                    resolvedType = mappings.get( resolvedType );
                }
                return resolvedType;
            }

            List<Type> types = new ArrayList<>();
            for( Type type : current.getGenericInterfaces() )
            {
                Iterable<Type> interfaces = Classes.INTERFACES_OF.map( type );
                for( Type anInterface : interfaces )
                {
                    if( !types.contains( anInterface ) )
                    {
                        types.add( anInterface );
                    }
                }
                types.add( type );
            }

            if( current.getGenericSuperclass() != null )
            {
                types.add( current.getGenericSuperclass() );
            }

            for( Type type : types )
            {
                Class subClass;
                if( type instanceof ParameterizedType )
                {
                    ParameterizedType pt = (ParameterizedType) type;
                    Type[] args = pt.getActualTypeArguments();
                    Class clazz = (Class) pt.getRawType();
                    TypeVariable[] vars = clazz.getTypeParameters();
                    for( int i = 0; i < vars.length; i++ )
                    {
                        TypeVariable var = vars[ i];
                        Type mappedType = args[ i];
                        mappings.put( var, mappedType );
                    }
                    subClass = (Class) pt.getRawType();
                }
                else
                {
                    subClass = (Class) type;
                }

                Type resolvedType = resolveTypeVariable( name, declaringClass, mappings, subClass );
                if( resolvedType != null )
                {
                    return resolvedType;
                }
            }

            return null;
        }

        /**
         * Get URI for a class.
         *
         * @param clazz class
         *
         * @return URI
         *
         * @throws NullPointerException if clazz is null
         */
        @SuppressWarnings( "raw" )
        public static String toURI( final Class clazz )
            throws NullPointerException
        {
            return toURI( clazz.getName() );
        }

        /**
         * Get URI for a class name.
         * <p>
         * Example:
         * </p>
         * <p>
         * Class name com.example.Foo$Bar is converted to URI urn:qi4j:com.example.Foo-Bar
         * </p>
         *
         * @param className class name
         *
         * @return URI
         *
         * @throws NullPointerException if className is null
         */
        public static String toURI( String className )
            throws NullPointerException
        {
            className = normalizeClassToURI( className );
            return "urn:qi4j:type:" + className;
        }

        /**
         * Get class name from a URI
         *
         * @param uri URI
         *
         * @return class name
         *
         * @throws NullPointerException if uri is null
         */
        public static String toClassName( String uri )
            throws NullPointerException
        {
            uri = uri.substring( "urn:qi4j:type:".length() );
            uri = denormalizeURIToClass( uri );
            return uri;
        }

        public static String normalizeClassToURI( String className )
        {
            return className.replace( '$', '-' );
        }

        public static String denormalizeURIToClass( String uriPart )
        {
            return uriPart.replace( '-', '$' );
        }

        public static Specification<ModelDescriptor> modelTypeSpecification( final String className )
        {
            return new Specification<ModelDescriptor>()
            {
                @Override
                public boolean satisfiedBy( ModelDescriptor item )
                {
                    return matchesAny( new Specification<String>()
                    {
                        @Override
                        public boolean satisfiedBy( String item )
                        {
                            return item.equals( className );
                        }
                    }, map( new Function<Class<?>, String>()
                    {
                        @Override
                        public String map( Class<?> item )
                        {
                            return item.getName();
                        }
                    }, item.types() ) );
                }
            };
        }

        @SuppressWarnings( "raw" )
        public static Specification<ModelDescriptor> exactTypeSpecification( final Class type )
        {
            return new Specification<ModelDescriptor>()
            {
                @Override
                public boolean satisfiedBy( ModelDescriptor item )
                {
                    return matchesAny( new Specification<Class<?>>()
                    {
                        @Override
                        public boolean satisfiedBy( Class<?> item )
                        {
                            return item.equals( type );
                        }
                    }, item.types() );
                }
            };
        }

        @SuppressWarnings( "raw" )
        public static Specification<ModelDescriptor> assignableTypeSpecification( final Class type )
        {
            return new Specification<ModelDescriptor>()
            {
                @Override
                public boolean satisfiedBy( ModelDescriptor item )
                {
                    return matchesAny( new Specification<Class<?>>()
                    {
                        @Override
                        @SuppressWarnings( "unchecked" )
                        public boolean satisfiedBy( Class<?> itemType )
                        {
                            return !type.equals( itemType ) && type.isAssignableFrom( itemType );
                        }
                    }, item.types() );
                }
            };
        }

        @SuppressWarnings( "raw" )
        public static String toString( Iterable<? extends Class> type )
        {
            StringBuilder builder = new StringBuilder();
            builder.append( "[" );
            boolean first = true;
            for( Class c : type )
            {
                if( !first )
                {
                    builder.append( "," );
                }
                first = false;
                builder.append( c.getSimpleName() );
            }
            builder.append( "]" );
            return builder.toString();
        }

        public static Function<Type, String> toClassName()
        {
            return new Function<Type, String>()
            {
                @Override
                public String map( Type type )
                {
                    return RAW_CLASS.map( type ).getName();
                }
            };
        }

        private Classes()
        {
        }
    }