alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (CoreReflectionFactory.java)

This example Java source code file (CoreReflectionFactory.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

abstracttypemirror, annotation, arraylist, class, illegalargumentexception, list, name, override, parameterizedtype, primitivetype, r\,p, reflection, reflectionelement, string, typemirror, unsupportedoperationexception, util

The CoreReflectionFactory.java Java example source code

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.lang.annotation.Annotation;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import java.lang.reflect.*;
import java.io.Writer;
import java.util.*;

import static javax.lang.model.SourceVersion.RELEASE_8;
import static java.util.Objects.*;

/**
 * This class provides a proof-of-concept implementation of the {@code
 * javax.lang.model.*} API backed by core reflection. That is, rather
 * than having a source file or compile-time class file as the
 * originator of the information about an element or type, as done
 * during standard annotation processing, runtime core reflection
 * objects serve that purpose instead.
 *
 * With this kind of implementation, the same logic can be used for
 * both compile-time and runtime processing of annotations.
 *
 * The nested types in this class define a specialization of {@code
 * javax.lang.model.*} to provide some additional functionality and
 * type information. The original {@code javax.lang.model.*} API was
 * designed to accommodate such a specialization by using wildcards in
 * the return types of methods.
 *
 * It would be technically possible for further specializations of the
 * API implemented in this class to define alternative semantics of
 * annotation look-up. For example to allow one annotation to have the
 * effect of macro-expanding into a set of other annotations.
 *
 * Some aspects of the implementation are left as "exercises for the
 * reader" to complete if interested.
 *
 * When passed null pointers, the methods defined in this type will
 * generally throw null pointer exceptions.
 *
 * To get started, first compile this file with a command line like:
 *
 * <pre>
 * $JDK/bin/javac -parameters -Xdoclint:all/public -Xlint:all -d $OUTPUT_DIR CoreReflectionFactory.java
 * </pre>
 *
 * and then run the main method of {@code CoreReflectionFactory},
 * which will print out a representation of {@code
 * CoreReflectionFactory}. To use the printing logic defined in {@code
 * javac}, put {@code tools.jar} on the classpath as in:
 *
 * <pre>
 * $JDK/bin/java -cp $OUTPUT_DIR:$JDK_ROOT/lib/tools.jar CoreReflectionFactory
 * </pre>
 *
 * @author Joseph D. Darcy (darcy)
 * @author Joel Borggren-Franck (jfranck)
 */
public class CoreReflectionFactory {
    private CoreReflectionFactory() {
        throw new AssertionError("No instances of CoreReflectionFactory for you!");
    }

    /**
     * Returns a reflection type element mirroring a {@code Class} object.
     * @return a reflection type element mirroring a {@code Class} object
     * @param clazz the {@code Class} to mirror
     */
    public static ReflectionTypeElement createMirror(Class<?> clazz) {
        return new CoreReflTypeElement(Objects.requireNonNull(clazz));
    }

    /**
     * Returns a reflection package element mirroring a {@code Package} object.
     * @return a reflection package element mirroring a {@code Package} object
     * @param pkg the {@code Package} to mirror
     */
    public static ReflectionPackageElement createMirror(Package pkg) {
        // Treat a null pkg to mean an unnamed package.
        return new CoreReflPackageElement(pkg);
    }

    /**
     * Returns a reflection variable element mirroring a {@code Field} object.
     * @return a reflection variable element mirroring a {@code Field} object
     * @param field the {@code Field} to mirror
     */
    public static ReflectionVariableElement createMirror(Field field) {
        return new CoreReflFieldVariableElement(Objects.requireNonNull(field));
    }

    /**
     * Returns a reflection executable element mirroring a {@code Method} object.
     * @return a reflection executable element mirroring a {@code Method} object
     * @param method the {@code Method} to mirror
     */
    public static ReflectionExecutableElement createMirror(Method method)  {
        return new CoreReflMethodExecutableElement(Objects.requireNonNull(method));
    }

    /**
     * Returns a reflection executable element mirroring a {@code Constructor} object.
     * @return a reflection executable element mirroring a {@code Constructor} object
     * @param constructor the {@code Constructor} to mirror
     */
    public static ReflectionExecutableElement createMirror(Constructor<?> constructor)  {
        return new CoreReflConstructorExecutableElement(Objects.requireNonNull(constructor));
    }

    /**
     * Returns a type parameter element mirroring a {@code TypeVariable} object.
     * @return a type parameter element mirroring a {@code TypeVariable} object
     * @param tv the {@code TypeVariable} to mirror
     */
    public static TypeParameterElement createMirror(java.lang.reflect.TypeVariable<?> tv) {
        return new CoreReflTypeParameterElement(Objects.requireNonNull(tv));
    }

    /**
     * Returns a variable element mirroring a {@code Parameter} object.
     * @return a variable element mirroring a {@code Parameter} object
     * @param p the {Parameter} to mirror
     */
    public static VariableElement createMirror(java.lang.reflect.Parameter p) {
        return new CoreReflParameterVariableElement(Objects.requireNonNull(p));
    }

    /**
     * Returns an annotation mirror mirroring an annotation object.
     * @return an annotation mirror mirroring an annotation object
     * @param annotation the annotation to mirror
     */
    public static AnnotationMirror createMirror(Annotation annotation)  {
        return new CoreReflAnnotationMirror(Objects.requireNonNull(annotation));
    }

    /**
     * Returns a {@code Types} utility object for type objects backed by core reflection.
     * @return a {@code Types} utility object for type objects backed by core reflection
     */
    public static Types getTypes() {
        return CoreReflTypes.instance();
    }

    /**
     * Returns an {@code Elements} utility object for type objects backed by core reflection.
     * @return an {@code Elements} utility object for type objects backed by core reflection
     */
    public static Elements getElements() {
        return CoreReflElements.instance();
    }

    // Helper
    private static TypeMirror createTypeMirror(Class<?> c) {
        return TypeFactory.instance(Objects.requireNonNull(c));
    }

    /**
     * Main method; prints out a representation of this class.
     * @param args command-line arguments, currently ignored
     */
    public static void main(String... args) {
        getElements().printElements(new java.io.PrintWriter(System.out),
                                    createMirror(CoreReflectionFactory.class));
    }

    /**
     * A specialization of {@code javax.lang.model.element.Element} that is
     * backed by core reflection.
     */
    public static interface ReflectionElement
        extends Element, AnnotatedElement {

        /**
         * {@inheritDoc}
         */
        @Override
        ReflectionElement getEnclosingElement();

        /**
         * {@inheritDoc}
         */
        @Override
        List<ReflectionElement> getEnclosedElements();

        /**
         * Applies a visitor to this element.
         *
         * @param v the visitor operating on this element
         * @param p additional parameter to the visitor
         * @param <R> the return type of the visitor's methods
         * @param <P> the type of the additional parameter to the visitor's methods
         * @return a visitor-specified result
         */
        <R,P> R accept(ReflectionElementVisitor v, P p);

        // Functionality specific to the specialization
        /**
         * Returns the underlying core reflection source object, if applicable.
         * @return the underlying core reflection source object, if applicable
         */
        AnnotatedElement getSource();

        // Functionality from javax.lang.model.util.Elements
        /**
         * Returns the package of an element. The package of a package
         * is itself.
         * @return the package of an element
         */
        ReflectionPackageElement getPackage();

    }

    /**
     * A logical specialization of {@code
     * javax.lang.model.element.ElementVisitor} being backed by core
     * reflection.
     *
     * @param <R> the return type of this visitor's methods.
     * @param <P> the type of the additional parameter to this visitor's
     *            methods.
     */
    public static interface ReflectionElementVisitor<R, P> {
        /**
         * Visits an element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visit(ReflectionElement e, P p);

        /**
         * A convenience method equivalent to {@code v.visit(e, null)}.
         * @param e  the element to visit
         * @return a visitor-specified result
         */
        R visit(ReflectionElement e);

        /**
         * Visits a package element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitPackage(ReflectionPackageElement e, P p);

        /**
         * Visits a type element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitType(ReflectionTypeElement e, P p);

        /**
         * Visits a variable element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitVariable(ReflectionVariableElement e, P p);

        /**
         * Visits an executable element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitExecutable(ReflectionExecutableElement e, P p);

        /**
         * Visits a type parameter element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitTypeParameter(ReflectionTypeParameterElement e, P p);

        /**
         * Visits an unknown kind of element.
         * This can occur if the language evolves and new kinds
         * of elements are added to the {@code Element} hierarchy.
         *
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         * @throws UnknownElementException
         * a visitor implementation may optionally throw this exception
         */
        R visitUnknown(ReflectionElement e, P p);
    }

    /**
     * A specialization of {@code javax.lang.model.element.ExecutableElement} that is
     * backed by core reflection.
     */
    public static interface ReflectionExecutableElement
        extends ReflectionElement, ExecutableElement, ReflectionParameterizable {

        /**
         * {@inheritDoc}
         */
        @Override
        List<ReflectionTypeParameterElement> getTypeParameters();

        /**
         * {@inheritDoc}
         */
        @Override
        List<ReflectionVariableElement> getParameters();

        // Functionality specific to the specialization
        /**
         * Returns all parameters, including synthetic ones.
         * @return all parameters, including synthetic ones
         */
        List<ReflectionVariableElement> getAllParameters();

        /**
         * {@inheritDoc}
         */
        @Override
        Executable getSource();

        /**
         * Returns true if this executable is a synthetic construct; returns false otherwise.
         * @return true if this executable is a synthetic construct; returns false otherwise
         */
        boolean isSynthetic();

        /**
         * Returns true if this executable is a bridge method; returns false otherwise.
         * @return true if this executable is a bridge method; returns false otherwise
         */
        boolean isBridge();
    }

    /**
     * A specialization of {@code javax.lang.model.element.PackageElement} being
     * backed by core reflection.
     */
    public static interface ReflectionPackageElement
        extends ReflectionElement, PackageElement {

        // Functionality specific to the specialization
        /**
         * {@inheritDoc}
         */
        @Override
        Package getSource();
    }

    /**
     * A specialization of {@code javax.lang.model.element.TypeElement} that is
     * backed by core reflection.
     */
    public static interface ReflectionTypeElement
        extends ReflectionElement, TypeElement, ReflectionParameterizable {

        /**
         * {@inheritDoc}
         */
        @Override
        List<ReflectionTypeParameterElement> getTypeParameters();

        /**
         * {@inheritDoc}
         */
        @Override
        List<ReflectionElement> getEnclosedElements();

        // Methods specific to the specialization, but functionality
        // also present in javax.lang.model.util.Elements.
        /**
         * Returns all members of a type element, whether inherited or
         * declared directly. For a class the result also includes its
         * constructors, but not local or anonymous classes.
         * @return all members of the type
         */
        List<ReflectionElement> getAllMembers();

        /**
         * Returns the binary name of a type element.
         * @return the binary name of a type element
         */
        Name getBinaryName();

        // Functionality specific to the specialization
        /**
         * {@inheritDoc}
         */
        @Override
        Class<?> getSource();
    }

    /**
     * A specialization of {@code javax.lang.model.element.TypeParameterElement} being
     * backed by core reflection.
     */
    public static interface ReflectionTypeParameterElement
        extends ReflectionElement, TypeParameterElement {

        /**
         * {@inheritDoc}
         */
        @Override
        ReflectionElement getGenericElement();

        // Functionality specific to the specialization
        /**
         * {@inheritDoc}
         */
        @Override
        java.lang.reflect.TypeVariable<?> getSource();
    }

    /**
     * A specialization of {@code javax.lang.model.element.VariableElement} that is
     * backed by core reflection.
     */
    public static interface ReflectionVariableElement
        extends ReflectionElement, VariableElement {

        // Functionality specific to the specialization
        /**
         * Returns true if this variable is a synthetic construct; returns false otherwise.
         * @return true if this variable is a synthetic construct; returns false otherwise
         */
        boolean isSynthetic();

        /**
         * Returns true if this variable is implicitly declared in source code; returns false otherwise.
         * @return true if this variable is implicitly declared in source code; returns false otherwise
         */
        boolean isImplicit();

        // The VariableElement concept covers fields, variables, and
        // method and constructor parameters. Therefore, this
        // interface cannot define a more precise override of
        // getSource since those three concept have different core
        // reflection types with no supertype more precise than
        // AnnotatedElement.
    }

    /**
     * A specialization of {@code javax.lang.model.element.Parameterizable} being
     * backed by core reflection.
     */
    public static interface ReflectionParameterizable
        extends ReflectionElement, Parameterizable {
        @Override
        List<ReflectionTypeParameterElement> getTypeParameters();
    }

    /**
     * Base class for concrete visitors of elements backed by core reflection.
     */
    public static abstract class AbstractReflectionElementVisitor8<R, P>
        extends AbstractElementVisitor8<R, P>
        implements ReflectionElementVisitor<R, P> {
        protected AbstractReflectionElementVisitor8() {
            super();
        }
    }

    /**
     * Base class for simple visitors of elements that are backed by core reflection.
     */
    @SupportedSourceVersion(value=RELEASE_8)
    public static abstract class SimpleReflectionElementVisitor8<R, P>
        extends SimpleElementVisitor8<R, P>
        implements ReflectionElementVisitor<R, P> {

        protected SimpleReflectionElementVisitor8(){
            super();
        }

        protected SimpleReflectionElementVisitor8(R defaultValue) {
            super(defaultValue);
        }

        // Create manual "bridge methods" for now.

        @Override
        public final R visitPackage(PackageElement e, P p) {
            return visitPackage((ReflectionPackageElement) e , p);
        }

        @Override
        public final R visitType(TypeElement e, P p) {
            return visitType((ReflectionTypeElement) e , p);
        }

        @Override
        public final R visitVariable(VariableElement e, P p) {
            return visitVariable((ReflectionVariableElement) e , p);
        }

        @Override
        public final R visitExecutable(ExecutableElement e, P p) {
            return visitExecutable((ReflectionExecutableElement) e , p);
        }

        @Override
        public final R visitTypeParameter(TypeParameterElement e, P p) {
            return visitTypeParameter((ReflectionTypeParameterElement) e , p);
        }
    }

    /**
     * {@inheritDoc}
     */
    public static interface ReflectionElements  extends Elements {
        /**
         * Returns the innermost enclosing {@link ReflectionTypeElement}
         * of the {@link ReflectionElement} or {@code null} if the
         * supplied ReflectionElement is toplevel or represents a
         * Package.
         *
         * @param e the {@link ReflectionElement} whose innermost
         * enclosing {@link ReflectionTypeElement} is sought
         * @return the innermost enclosing {@link
         * ReflectionTypeElement} or @{code null} if the parameter
         * {@code e} is a toplevel element or a package
         */
        ReflectionTypeElement getEnclosingTypeElement(ReflectionElement e);

        /**
         * {@inheritDoc}
         */
        @Override
        List<? extends ReflectionElement> getAllMembers(TypeElement type);

        /**
         * {@inheritDoc}
         */
        @Override
        ReflectionPackageElement getPackageElement(CharSequence name);

        /**
         * {@inheritDoc}
         */
        @Override
        ReflectionPackageElement getPackageOf(Element type);

        /**
         * {@inheritDoc}
         */
        @Override
        ReflectionTypeElement getTypeElement(CharSequence name);
    }

    // ------------------------- Implementation classes ------------------------

    // Exercise for the reader: review the CoreReflElement class
    // hierarchy below with an eye toward exposing it as an extensible
    // API that could be subclassed to provide customized behavior,
    // such as alternate annotation lookup semantics.

    private static abstract class CoreReflElement
        implements ReflectionElement, AnnotatedElement {
        public abstract AnnotatedElement getSource();

        protected CoreReflElement() {
            super();
        }

        // ReflectionElement methods
        @Override
        public ReflectionPackageElement getPackage() {
            throw new UnsupportedOperationException();
        }

        @Override
        public TypeMirror asType() {
            throw new UnsupportedOperationException(getClass().toString());
        }

        @Override
        public List<? extends AnnotationMirror> getAnnotationMirrors() {
            Annotation[] annotations = getSource().getDeclaredAnnotations();
            int len = annotations.length;

            if (len > 0) {
                List<AnnotationMirror> res = new ArrayList<>(len);
                for (Annotation a : annotations) {
                    res.add(createMirror(a));
                }
                return Collections.unmodifiableList(res);
            } else {
                return Collections.emptyList();
            }
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(0, false);
        }

        @Override
        public abstract Name getSimpleName();

        @Override
        public abstract ReflectionElement getEnclosingElement();

        @Override
        public abstract List<ReflectionElement> getEnclosedElements();

        //AnnotatedElement methods
        @Override
        public <T extends Annotation> T getAnnotation(Class annotationClass) {
            return getSource().getAnnotation(annotationClass);
        }

        @Override
        public <T extends Annotation> T[] getAnnotationsByType(Class annotationClass) {
            return getSource().getAnnotationsByType(annotationClass);
        }

        @Override
        public Annotation[] getAnnotations() {
            return getSource().getAnnotations();
        }

        @Override
        public <T extends Annotation> T getDeclaredAnnotation(Class annotationClass) {
            return getSource().getDeclaredAnnotation(annotationClass);
        }

        @Override
        public <T extends Annotation> T[] getDeclaredAnnotationsByType(Class annotationClass) {
            return getSource().getDeclaredAnnotationsByType(annotationClass);
        }

        @Override
        public Annotation[] getDeclaredAnnotations() {
            return getSource().getDeclaredAnnotations();
        }

        // java.lang.Object methods
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof CoreReflElement) {
                CoreReflElement other = (CoreReflElement)obj;
                return Objects.equals(other.getSource(), this.getSource());
            }
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(getSource());
        }

        @Override
        public String toString() {
            return getKind().toString() + " " + getSimpleName().toString();
        }
    }

    // Type
    private static class CoreReflTypeElement extends CoreReflElement
        implements ReflectionTypeElement {
        private final Class<?> source;

        protected CoreReflTypeElement(Class<?> source) {
            Objects.requireNonNull(source);
            if (source.isPrimitive() ||
                source.isArray()) {
                throw new IllegalArgumentException("Cannot create a ReflectionTypeElement based on class: " + source);
            }

            this.source = source;
        }

        @Override
        public TypeMirror asType() {
            return createTypeMirror(source);
        }

        @Override
        public Class<?> getSource() {
            return source;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflTypeElement) {
                return source.equals(((CoreReflTypeElement)o).getSource());
            } else {
                return false;
            }
        }

        @Override
        public <R,P> R accept(ElementVisitor v, P p) {
            return v.visitType(this, p);
        }

        @Override
        public <R,P> R accept(ReflectionElementVisitor v, P p) {
            return v.visitType(this, p);
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(source.getModifiers() &
                                         (source.isInterface() ?
                                          java.lang.reflect.Modifier.interfaceModifiers() :
                                          java.lang.reflect.Modifier.classModifiers()),
                                         false);
        }

        @Override
        public List<ReflectionElement> getEnclosedElements() {
            List<ReflectionElement> enclosedElements = new ArrayList<>();

            for (Class<?> declaredClass : source.getDeclaredClasses()) {
                enclosedElements.add(createMirror(declaredClass));
            }

            // Add elements in the conventional ordering: fields, then
            // constructors, then methods.
            for (Field f : source.getDeclaredFields()) {
                enclosedElements.add(createMirror(f));
            }

            for (Constructor<?> c : source.getDeclaredConstructors()) {
                enclosedElements.add(createMirror(c));
            }

            for (Method m : source.getDeclaredMethods()) {
                enclosedElements.add(createMirror(m));
            }

            return (enclosedElements.isEmpty() ?
                    Collections.emptyList():
                    Collections.unmodifiableList(enclosedElements));
        }

        // Review for default method handling.
        @Override
        public List<ReflectionElement> getAllMembers() {
            List<ReflectionElement> allMembers = new ArrayList<>();

            // If I only had a MultiMap ...
            List<ReflectionElement> fields = new ArrayList<>();
            List<ReflectionExecutableElement> methods = new ArrayList<>();
            List<ReflectionElement> classes = new ArrayList<>();

            // Add all fields for this class
            for (Field f : source.getDeclaredFields()) {
                fields.add(createMirror(f));
            }

            // Add all methods for this class
            for (Method m : source.getDeclaredMethods()) {
                methods.add(createMirror(m));
            }

            // Add all classes for this class, except anonymous/local as per Elements.getAllMembers doc
            for (Class<?> c : source.getDeclaredClasses()) {
                if (c.isLocalClass() || c.isAnonymousClass())
                    continue;
                classes.add(createMirror(c));
            }

            Class<?> cls = source;
            if (cls.isInterface()) {
                cls = null;
            }
            do {
                // Walk up superclasses adding non-private elements.
                // If source is an interface, just add Object's
                // elements.

                if (cls == null) {
                    cls = java.lang.Object.class;
                } else {
                    cls = cls.getSuperclass();
                }

                addMembers(cls, fields, methods, classes);

            } while (cls != java.lang.Object.class);

            // add members on (super)interface(s)
            Set<Class seenInterfaces = new HashSet<>();
            Queue<Class interfaces = new LinkedList<>();
            if (source.isInterface()) {
                seenInterfaces.add(source);
                interfaces.add(source);
            } else {
                Class<?>[] ifaces = source.getInterfaces();
                for (Class<?> iface : ifaces) {
                    seenInterfaces.add(iface);
                    interfaces.add(iface);
                }
            }

            while (interfaces.peek() != null) {
                Class<?> head = interfaces.remove();
                addMembers(head, fields, methods, classes);

                Class<?>[] ifaces = head.getInterfaces();
                for (Class<?> iface : ifaces) {
                    if (!seenInterfaces.contains(iface)) {
                        seenInterfaces.add(iface);
                        interfaces.add(iface);
                    }
                }
            }

            // Add constructors
            for (Constructor<?> c : source.getDeclaredConstructors()) {
                allMembers.add(createMirror(c));
            }

            // Add all unique methods
            allMembers.addAll(methods);

            // Add all unique fields
            allMembers.addAll(fields);

            // Add all unique classes
            allMembers.addAll(classes);

            return Collections.unmodifiableList(allMembers);
        }

        private void addMembers(Class<?> cls,
                                List<ReflectionElement> fields,
                                List<ReflectionExecutableElement> methods,
                                List<ReflectionElement> classes) {
            Elements elements = getElements();

            for (Field f : cls.getDeclaredFields()) {
                if (java.lang.reflect.Modifier.isPrivate(f.getModifiers())) { continue; }
                ReflectionElement tmp = createMirror(f);
                boolean add = true;
                for (ReflectionElement e : fields) {
                    if (elements.hides(e, tmp)) {
                        add = false;
                        break;
                    }
                }
                if (add) {
                    fields.add(tmp);
                }
            }

            for (Method m : cls.getDeclaredMethods()) {
                if (java.lang.reflect.Modifier.isPrivate(m.getModifiers()))
                    continue;

                ReflectionExecutableElement tmp = createMirror(m);
                boolean add = true;
                for (ReflectionExecutableElement e : methods) {
                    if (elements.hides(e, tmp)) {
                        add = false;
                        break;
                    } else if (elements.overrides(e, tmp, this)) {
                        add = false;
                        break;
                    }
                }
                if (add) {
                    methods.add(tmp);
                }
            }

            for (Class<?> c : cls.getDeclaredClasses()) {
                if (java.lang.reflect.Modifier.isPrivate(c.getModifiers()) ||
                    c.isLocalClass() ||
                    c.isAnonymousClass())
                    continue;

                ReflectionElement tmp = createMirror(c);
                boolean add = true;
                for (ReflectionElement e : classes) {
                    if (elements.hides(e, tmp)) {
                        add = false;
                        break;
                    }
                }
                if (add) {
                    classes.add(tmp);
                }
            }
        }

        @Override
        public ElementKind getKind() {
            if (source.isInterface()) {
                if (source.isAnnotation())
                    return ElementKind.ANNOTATION_TYPE;
                else
                    return ElementKind.INTERFACE;
            } else if (source.isEnum()) {
                return ElementKind.ENUM;
            } else
                return ElementKind.CLASS;
        }

        @Override
        public NestingKind getNestingKind() {
            if (source.isAnonymousClass())
                return NestingKind.ANONYMOUS;
            else if (source.isLocalClass())
                return NestingKind.LOCAL;
            else if (source.isMemberClass())
                return NestingKind.MEMBER;
            else return
                NestingKind.TOP_LEVEL;
        }

        @Override
        public Name getQualifiedName() {
            String name = source.getCanonicalName(); // TODO, this should be a FQN for
                                                     // the current element
            if (name == null)
                name = "";
            return StringName.instance(name);
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance(source.getSimpleName());
        }

        @Override
        public TypeMirror getSuperclass() {
            if (source.equals(java.lang.Object.class)) {
                return NoType.getNoneInstance();
            } else {
                return createTypeMirror(source.getSuperclass());
            }
        }

        @Override
        public List<? extends TypeMirror> getInterfaces() {
            Class[] interfaces = source.getInterfaces();
            int len = interfaces.length;
            List<TypeMirror> res = new ArrayList<>(len);

            if (len > 0) {
                for (Class<?> c : interfaces) {
                    res.add(createTypeMirror(c));
                }
            } else {
                return Collections.emptyList();
            }
            return Collections.unmodifiableList(res);
        }

        @Override
        public List<ReflectionTypeParameterElement> getTypeParameters() {
            return createTypeParameterList(source);
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            // Returns the package of a top-level type and returns the
            // immediately lexically enclosing element for a nested type.

            switch(getNestingKind()) {
            case TOP_LEVEL:
                return createMirror(source.getPackage());
            case MEMBER:
                return createMirror(source.getEnclosingClass());
            default:
                if (source.getEnclosingConstructor() != null) {
                    return createMirror(source.getEnclosingConstructor());
                } else if (source.getEnclosingMethod() != null) {
                    return createMirror(source.getEnclosingMethod());
                } else {
                    return createMirror(source.getEnclosingClass());
                }
            }
        }

        @Override
        public Name getBinaryName() {
            return StringName.instance(getSource().getName());
        }
    }

    private static abstract class CoreReflExecutableElement extends CoreReflElement
        implements ReflectionExecutableElement {

        protected Executable source = null;
        protected final List<CoreReflParameterVariableElement> parameters;

        protected CoreReflExecutableElement(Executable source,
                                            List<CoreReflParameterVariableElement> parameters) {
            this.source = Objects.requireNonNull(source);
            this.parameters = Objects.requireNonNull(parameters);
        }

        @Override
        public <R,P> R accept(ElementVisitor v, P p) {
            return v.visitExecutable(this, p);
        }

        @Override
        public <R,P> R accept(ReflectionElementVisitor v, P p) {
            return v.visitExecutable(this, p);
        }

        @Override
        public abstract ExecutableType asType();

        // Only Types and Packages enclose elements; see Element.getEnclosedElements()
        @Override
        public List<ReflectionElement> getEnclosedElements() {
            return Collections.emptyList();
        }

        @Override
        public List<ReflectionVariableElement> getParameters() {
            List<ReflectionVariableElement> tmp = new ArrayList<>();
            for (ReflectionVariableElement parameter : parameters) {
                if (!parameter.isSynthetic())
                    tmp.add(parameter);
            }
            return tmp;
        }

        @Override
        public List<ReflectionVariableElement> getAllParameters() {
            // Could "fix" this if the return type included wildcards
            @SuppressWarnings("unchecked")
            List<ReflectionVariableElement> tmp = (List)(List)parameters;
            return tmp;
        }

        @Override
        public List<? extends TypeMirror> getThrownTypes() {
            Class<?>[] thrown = source.getExceptionTypes();
            int len = thrown.length;
            List<TypeMirror> res = new ArrayList<>(len);

            if (len > 0) {
                for (Class<?> c : thrown) {
                    res.add(createTypeMirror(c));
                }
            } else {
                return Collections.emptyList();
            }
            return Collections.unmodifiableList(res);
        }

        @Override
        public boolean isVarArgs() {
            return source.isVarArgs();
        }

        @Override
        public boolean isSynthetic() {
            return source.isSynthetic();
        }

        @Override
        public boolean isBridge() {
            return false;
        }

        @Override
        public List<ReflectionTypeParameterElement> getTypeParameters() {
            return createTypeParameterList(source);
        }

        public abstract AnnotationValue getDefaultValue();

        @Override
        public TypeMirror getReceiverType() {
            // New in JDK 8
            throw new UnsupportedOperationException(this.toString());
        }
    }

    private static class CoreReflConstructorExecutableElement
        extends CoreReflExecutableElement {

        protected CoreReflConstructorExecutableElement(Constructor<?> source) {
            super(Objects.requireNonNull(source),
                  createParameterList(source));
        }

        @Override
        public  Constructor<?> getSource() {
            return (Constructor<?>)source;
        }

        @Override
        public TypeMirror getReturnType() {
            return NoType.getVoidInstance();
        }

        @Override
        public ExecutableType asType() {
            throw new UnsupportedOperationException(getClass().toString());
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflConstructorExecutableElement) {
                return source.equals(((CoreReflConstructorExecutableElement)o).getSource());
            } else {
                return false;
            }
        }

        @Override
        public ElementKind getKind() {
            return ElementKind.CONSTRUCTOR;
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(source.getModifiers() &
                                         java.lang.reflect.Modifier.constructorModifiers(), false);
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            return createMirror(source.getDeclaringClass());
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance("<init>");
        }

        @Override
        public AnnotationValue getDefaultValue() {
            // a constructor is never an annotation element
            return null;
        }

        @Override
        public boolean isDefault() {
            return false; // A constructor cannot be a default method
        }
    }

    private static class CoreReflMethodExecutableElement
        extends CoreReflExecutableElement {

        protected CoreReflMethodExecutableElement(Method source) {
            super(Objects.requireNonNull(source),
                  createParameterList(source));
            this.source = source;
        }

        @Override
        public Method getSource() {
            return (Method)source;
        }

        @Override
        public TypeMirror getReturnType() {
            return TypeFactory.instance(getSource().getReturnType());
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflMethodExecutableElement) {
                return source.equals( ((CoreReflMethodExecutableElement)o).getSource());
            } else {
                return false;
            }
        }

        @Override
        public ElementKind getKind() {
            return ElementKind.METHOD;
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(source.getModifiers() &
                                         java.lang.reflect.Modifier.methodModifiers(),
                                         isDefault());
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            return createMirror(source.getDeclaringClass());
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance(source.getName());
        }

        @Override
        public AnnotationValue getDefaultValue() {
            Object value = getSource().getDefaultValue();
            if (null == value) {
                return null;
            } else {
                return new CoreReflAnnotationValue(value);
            }
        }

        @Override
        public boolean isDefault() {
            return getSource().isDefault();
        }

        @Override
        public boolean isBridge() {
            return getSource().isBridge();
        }

        @Override
        public ExecutableType asType() {
            return TypeFactory.instance(getSource());
        }
    }

    private static List<CoreReflParameterVariableElement> createParameterList(Executable source) {
        Parameter[] parameters = source.getParameters();
        int length = parameters.length;
        if (length == 0)
            return Collections.emptyList();
        else {
            List<CoreReflParameterVariableElement> tmp = new ArrayList<>(length);
            for (Parameter parameter : parameters) {
                tmp.add(new CoreReflParameterVariableElement(parameter));
            }
            return Collections.unmodifiableList(tmp);
        }
    }

    private static List<ReflectionTypeParameterElement> createTypeParameterList(GenericDeclaration source) {
        java.lang.reflect.TypeVariable<?>[] typeParams = source.getTypeParameters();
        int length = typeParams.length;
        if (length == 0)
            return Collections.emptyList();
        else {
            List<ReflectionTypeParameterElement> tmp = new ArrayList<>(length);
            for (java.lang.reflect.TypeVariable<?> typeVar : typeParams)
                tmp.add(new CoreReflTypeParameterElement(typeVar));
            return Collections.unmodifiableList(tmp);
        }
    }

    private static class CoreReflTypeParameterElement
        extends CoreReflElement
        implements ReflectionTypeParameterElement {

        private final GenericDeclaration source;
        private final java.lang.reflect.TypeVariable<?> sourceTypeVar;

        protected CoreReflTypeParameterElement(java.lang.reflect.TypeVariable<?> sourceTypeVar) {
            this.sourceTypeVar = Objects.requireNonNull(sourceTypeVar);
            this.source = Objects.requireNonNull(sourceTypeVar.getGenericDeclaration());
        }

        @Override
        public java.lang.reflect.TypeVariable<?> getSource() {
            return sourceTypeVar;
        }

        protected java.lang.reflect.TypeVariable<?> getSourceTypeVar() {
            return sourceTypeVar;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflTypeParameterElement) {
                return sourceTypeVar.equals(((CoreReflTypeParameterElement)o).sourceTypeVar);
            } else {
                return false;
            }
        }

        @Override
        public <R,P> R accept(ElementVisitor v, P p) {
            return v.visitTypeParameter(this, p);
        }

        @Override
        public <R,P> R accept(ReflectionElementVisitor v, P p) {
            return v.visitTypeParameter(this, p);
        }

        @Override
        public List<ReflectionElement> getEnclosedElements() {
            return Collections.emptyList();
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            if (source instanceof Class)
                return createMirror((Class<?>)source);
            else if (source instanceof Method)
                return createMirror((Method)source);
            else if (source instanceof Constructor)
                return createMirror((Constructor<?>)source);
            else
                throw new AssertionError("Unexpected enclosing element: " + source);
        }

        @Override
        public ElementKind getKind() {
            return ElementKind.TYPE_PARAMETER;
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance(sourceTypeVar.getName());
        }

        // TypeParameterElement methods
        @Override
        public ReflectionElement getGenericElement() {
            return getEnclosingElement(); // As per the doc,
                                          // getEnclosingElement and
                                          // getGenericElement return
                                          // the same information.
        }

        @Override
        public List<? extends TypeMirror> getBounds() {
            Type[] types = getSourceTypeVar().getBounds();
            int len = types.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList<>(len);
                for (Type t : types) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                return Collections.emptyList();
            }
        }
    }

    private abstract static class CoreReflVariableElement extends CoreReflElement
        implements ReflectionVariableElement {

        protected CoreReflVariableElement() {}

        // Element visitor
        @Override
        public <R,P> R accept(ElementVisitorv, P p) {
            return v.visitVariable(this, p);
        }

        // ReflectElement visitor
        @Override
        public <R,P> R accept(ReflectionElementVisitor v, P p) {
            return v.visitVariable(this, p);
        }

        @Override
        public List<ReflectionElement> getEnclosedElements() {
            return Collections.emptyList();
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            return null;
        }

        @Override
        public boolean isSynthetic() {
            return false;
        }

        @Override
        public boolean isImplicit() {
            return false;
        }
    }

    private static class CoreReflFieldVariableElement extends CoreReflVariableElement {
        private final Field source;

        protected CoreReflFieldVariableElement(Field source) {
            this.source = Objects.requireNonNull(source);
        }

        @Override
        public Field getSource() {
            return source;
        }

        @Override
        public TypeMirror asType() {
            return createTypeMirror(getSource().getType());
        }

        @Override
        public ElementKind getKind() {
            if (source.isEnumConstant())
                return ElementKind.ENUM_CONSTANT;
            else
                return ElementKind.FIELD;
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(source.getModifiers() &
                                         java.lang.reflect.Modifier.fieldModifiers(), false);
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance(source.getName());
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            return createMirror(source.getDeclaringClass());
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflFieldVariableElement) {
                return Objects.equals(source,
                                      ((CoreReflFieldVariableElement)o).getSource());
            } else {
                return false;
            }
        }

        @Override
        public Object getConstantValue() {
            Field target = source;

            // The api says only Strings and primitives may be compile time constants.
            // Ensure field is that, and final.
            //
            // Also, we don't have an instance so restrict to static Fields
            //
            if (!(source.getType().equals(java.lang.String.class)
                  || source.getType().isPrimitive())) {
                return null;
            }
            final int modifiers = target.getModifiers();
            if (!( java.lang.reflect.Modifier.isFinal(modifiers) &&
                   java.lang.reflect.Modifier.isStatic(modifiers))) {
                return null;
            }

            try {
                return target.get(null);
            } catch (IllegalAccessException e) {
                try {
                    target.setAccessible(true);
                    return target.get(null);
                } catch (IllegalAccessException i) {
                    throw new SecurityException(i);
                }
            }
        }
    }

    private static class CoreReflParameterVariableElement
        extends CoreReflVariableElement {
        private final Parameter source;

        protected CoreReflParameterVariableElement(Parameter source) {
            this.source = Objects.requireNonNull(source);
        }

        @Override
        public Parameter getSource() {
            return source;
        }

        @Override
        public Set<Modifier> getModifiers() {
            return ModifierUtil.instance(source.getModifiers() &
                                         java.lang.reflect.Modifier.parameterModifiers(), false);
        }

        @Override
        public TypeMirror asType() {
            // TODO : switch to parameterized type
            return createTypeMirror(source.getType());
        }

        @Override
        public ElementKind getKind() {
            return ElementKind.PARAMETER;
        }

        @Override
        public Name getSimpleName() {
            return StringName.instance(source.getName());
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            Executable enclosing = source.getDeclaringExecutable();
            if (enclosing instanceof Method)
                return createMirror((Method)enclosing);
            else if (enclosing instanceof Constructor)
                return createMirror((Constructor<?>)enclosing);
            else
                throw new AssertionError("Bad enclosing value.");
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflParameterVariableElement) {
                return source.equals(((CoreReflParameterVariableElement) o).getSource());
            } else
                return false;
        }

        // VariableElement methods
        @Override
        public Object getConstantValue() {
            return null;
        }

        @Override
        public boolean isSynthetic() {
            return source.isSynthetic();
        }

        @Override
        public boolean isImplicit() {
            return source.isImplicit();
        }
    }

    private static class CoreReflPackageElement extends CoreReflElement
        implements ReflectionPackageElement {

        private final Package source;

        protected CoreReflPackageElement(Package source) {
            this.source = source;
        }

        @Override
        public Package getSource() {
            return source;
        }

        @Override
        public <R,P> R accept(ElementVisitor v, P p) {
            return v.visitPackage(this, p);
        }

        @Override
        public <R,P> R accept(ReflectionElementVisitor v, P p) {
            return v.visitPackage(this, p);
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof CoreReflPackageElement) {
                return Objects.equals(source,
                                      ((CoreReflPackageElement)o).getSource());
            } else {
                return false;
            }
        }

        @Override
        public ElementKind getKind() {
            return ElementKind.PACKAGE;
        }

        @Override
        public ReflectionElement getEnclosingElement() {
            return null;
        }

        @Override
        public List<ReflectionElement> getEnclosedElements() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Name getQualifiedName() {
            return StringName.instance((source != null) ?
                                       source.getName() :
                                       "" );
        }

        @Override
        public Name getSimpleName() {
            String n = ((source != null) ?
                        source.getName() :
                        "");
            int index = n.lastIndexOf('.');
            if (index > 0) {
                return StringName.instance(n.substring(index + 1, n.length()));
            } else {
                return StringName.instance(n);
            }
        }

        @Override
        public boolean isUnnamed() {
            if (source != null) {
                String name = source.getName();
                return(name == null || name.isEmpty());
            } else
                return true;
        }
    }

    private static class CoreReflAnnotationMirror
        implements javax.lang.model.element.AnnotationMirror {
        private final Annotation annotation;

        protected CoreReflAnnotationMirror(Annotation annotation) {
            this.annotation = Objects.requireNonNull(annotation);
        }

        @Override
        public DeclaredType getAnnotationType() {
            return (DeclaredType)TypeFactory.instance(annotation.annotationType());
        }

        @Override
        public Map<? extends ReflectionExecutableElement, ? extends AnnotationValue> getElementValues() {
            // This differs from the javac implementation in that it returns default values

            Method[] elems = annotation.annotationType().getDeclaredMethods();
            int len = elems.length;

            if (len > 0) {
                Map<ReflectionExecutableElement, AnnotationValue> res = new HashMap<>();
                for (Method m : elems) {
                    AnnotationValue v;
                    try {
                        v = new CoreReflAnnotationValue(m.invoke(annotation));
                    } catch (IllegalAccessException e) {
                        try {
                            m.setAccessible(true);
                            v = new CoreReflAnnotationValue(m.invoke(annotation));
                        } catch (IllegalAccessException i) {
                            throw new SecurityException(i);
                        } catch (InvocationTargetException ee) {
                            throw new RuntimeException(ee);
                        }
                    } catch (InvocationTargetException ee) {
                        throw new RuntimeException(ee);
                    }
                    ReflectionExecutableElement e = createMirror(m);
                    res.put(e, v);
                }

                return Collections.unmodifiableMap(res);
            } else {
                return Collections.emptyMap();
            }
        }

        @Override
        public boolean equals(Object other) {
            if (other instanceof CoreReflAnnotationMirror) {
                return annotation.equals(((CoreReflAnnotationMirror)other).annotation);
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(annotation);
        }

        @Override
        public String toString() {
            return annotation.toString();
        }
    }

    private static class CoreReflAnnotationValue
        implements javax.lang.model.element.AnnotationValue {
        private Object value = null;

        protected CoreReflAnnotationValue(Object value) {
            // Is this constraint really necessary?
            Objects.requireNonNull(value);
            this.value = value;
        }

        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public String toString() {
            return value.toString();
        }

        @Override
        public <R,P> R accept(AnnotationValueVisitor v, P p) {
            return v.visit(this, p);
        }
    }

    // Helper utility classes

    private static class StringName implements Name {
        private String name;

        private StringName(String name) {
            this.name = Objects.requireNonNull(name);
        }

        public static StringName instance(String name) {
            return new StringName(name);
        }

        @Override
        public int length() {
            return name.length();
        }

        @Override
        public char charAt(int index) {
            return name.charAt(index);
        }

        @Override
        public CharSequence subSequence(int start, int end) {
            return name.subSequence(start, end);
        }

        @Override
        public String toString() {
            return name;
        }

        @Override
        public boolean equals(Object other) {
            if (other instanceof StringName) {
                return name.equals(((StringName) other).name);
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return name.hashCode();
        }

        @Override
        public boolean contentEquals(CharSequence cs) {
            return name.contentEquals(cs);
        }
    }

    /*
     * Given an {@code int} value of modifiers, return a proper immutable set
     * of {@code Modifier}s as a result.
     */
    private static class ModifierUtil {
        private ModifierUtil() {
            throw new AssertionError("No instances for you.");
        }

        // Exercise for the reader: explore if caching of sets of
        // Modifiers would be helpful.

        public static Set<Modifier> instance(int modifiers, boolean isDefault) {
            Set<Modifier> modSet = EnumSet.noneOf(Modifier.class);

            if (java.lang.reflect.Modifier.isAbstract(modifiers))
                modSet.add(Modifier.ABSTRACT);

            if (java.lang.reflect.Modifier.isFinal(modifiers))
                modSet.add(Modifier.FINAL);

            if (java.lang.reflect.Modifier.isNative(modifiers))
                modSet.add(Modifier.NATIVE);

            if (java.lang.reflect.Modifier.isPrivate(modifiers))
                modSet.add(Modifier.PRIVATE);

            if (java.lang.reflect.Modifier.isProtected(modifiers))
                modSet.add(Modifier.PROTECTED);

            if (java.lang.reflect.Modifier.isPublic(modifiers))
                modSet.add(Modifier.PUBLIC);

            if (java.lang.reflect.Modifier.isStatic(modifiers))
                modSet.add(Modifier.STATIC);

            if (java.lang.reflect.Modifier.isStrict(modifiers))
                modSet.add(Modifier.STRICTFP);

            if (java.lang.reflect.Modifier.isSynchronized(modifiers))
                modSet.add(Modifier.SYNCHRONIZED);

            if (java.lang.reflect.Modifier.isTransient(modifiers))
                modSet.add(Modifier.TRANSIENT);

            if (java.lang.reflect.Modifier.isVolatile(modifiers))
                modSet.add(Modifier.VOLATILE);

            if (isDefault)
                modSet.add(Modifier.DEFAULT);

            return Collections.unmodifiableSet(modSet);
        }
    }

    private abstract static class AbstractTypeMirror implements TypeMirror {
        private final TypeKind kind;

        protected AbstractTypeMirror(TypeKind kind) {
            this.kind = Objects.requireNonNull(kind);
        }

        @Override
        public TypeKind getKind() {
            return kind;
        }

        @Override
        public <R,P> R accept(TypeVisitor v, P p) {
            return v.visit(this, p);
        }

        //Types methods
        abstract List<? extends TypeMirror> directSuperTypes();

        TypeMirror capture() {
            // Exercise for the reader: make this abstract and implement in subtypes
            throw new UnsupportedOperationException();
        }

        TypeMirror erasure() {
            // Exercise for the reader: make this abstract and implement in subtypes
            throw new UnsupportedOperationException();
        }

        // Exercise for the reader: implement the AnnotatedConstruct methods
        @Override
        public List<? extends AnnotationMirror> getAnnotationMirrors() {
            throw new UnsupportedOperationException();
        }

        @Override
        public <T extends Annotation> T getAnnotation(Class annotationClass) {
            throw new UnsupportedOperationException();
        }

        @Override
        public <T extends Annotation> T[] getAnnotationsByType(Class annotationClass) {
            throw new UnsupportedOperationException();
        }
    }

    private static class CoreReflArrayType extends AbstractTypeMirror
        implements javax.lang.model.type.ArrayType,
                   Reifiable {
        private Class<?> source = null;
        private Class<?> component = null;
        private TypeMirror eagerComponent = null;

        protected CoreReflArrayType(Class<?> source) {
            super(TypeKind.ARRAY);
            this.source = source;
            this.component = source.getComponentType();
            this.eagerComponent = TypeFactory.instance(component);
        }

        public TypeMirror getComponentType() {
            return eagerComponent;
        }

        @Override
        public Class<?> getSource() {
            return source;
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            final TypeMirror componentType = getComponentType();
            final TypeMirror[] directSupers;

            // JLS v4 4.10.3
            if (componentType.getKind().isPrimitive() ||
                component.equals(java.lang.Object.class)) {
                directSupers = new TypeMirror[3];
                directSupers[0] = TypeFactory.instance(java.lang.Object.class);
                directSupers[1] = TypeFactory.instance(java.lang.Cloneable.class);
                directSupers[2] = TypeFactory.instance(java.io.Serializable.class);
            } else if (componentType.getKind() == TypeKind.ARRAY) {
                List<? extends TypeMirror> componentDirectSupertypes = CoreReflTypes.instance().directSupertypes(componentType);
                directSupers = new TypeMirror[componentDirectSupertypes.size()];
                for (int i = 0; i < directSupers.length; i++) {
                    directSupers[i] = new CoreReflArrayType(Array.newInstance(((Reifiable)componentDirectSupertypes.get(i)).getSource(), 0).getClass());
                }
            } else {
                Class<?> superClass = component.getSuperclass();
                Class<?>[] interfaces = component.getInterfaces();
                directSupers = new TypeMirror[1 + interfaces.length];

                directSupers[0] = TypeFactory.instance(Array.newInstance(superClass, 0).getClass());

                for (int i = 0; i < interfaces.length; i++) {
                    directSupers[i + 1] = TypeFactory.instance(Array.newInstance(interfaces[i],0).getClass());
                }
            }

            return Collections.unmodifiableList(Arrays.asList(directSupers));
        }

        @Override
        public String toString() {
            return getKind() + " of " + getComponentType().toString();
        }
    }

    private static class CaptureTypeVariable extends AbstractTypeMirror implements javax.lang.model.type.TypeVariable {
        private TypeMirror source = null;
        private TypeMirror upperBound = null;
        private TypeMirror lowerBound = null;

        CaptureTypeVariable(TypeMirror source,
                            TypeMirror upperBound,
                            TypeMirror lowerBound) {
            super(TypeKind.TYPEVAR);

            this.source = Objects.requireNonNull(source);
            this.upperBound = (upperBound == null ? CoreReflTypes.instance().getNullType() : upperBound);
            this.lowerBound = (lowerBound == null ? CoreReflTypes.instance().getNullType() : lowerBound);
        }

        protected Class<?> getSource() {
            if (source instanceof CoreReflDeclaredType) {
                return ((CoreReflDeclaredType)source).getSource();
            } else {
                return null;
            }
        }

        @Override
        public TypeMirror getUpperBound() {
            return upperBound;
        }

        @Override
        public TypeMirror getLowerBound() {
            return lowerBound;
        }

        @Override
        public Element asElement() {
            if (null == getSource()) {
                return null;
            }
            return CoreReflectionFactory.createMirror(getSource());
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            throw new UnsupportedOperationException();

        }

        @Override
        public String toString() {
            return getKind() + " CAPTURE of: " + source.toString();
        }
    }

    private static class CoreReflElements implements ReflectionElements {
        private CoreReflElements() {} // mostly one instance for you

        private static CoreReflElements instance = new CoreReflElements();

        static CoreReflElements instance() {
            return instance;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public ReflectionPackageElement getPackageElement(CharSequence name) {
            return createMirror(Package.getPackage(name.toString()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public ReflectionTypeElement getTypeElement(CharSequence name) {
            // where name is a Canonical Name jls 6.7
            // but this method will probably accept an equivalent FQN
            // depending on Class.forName(String)

            ReflectionTypeElement tmp = null;

            // Filter out arrays
            String n = name.toString();
            if (n.contains("[")) return null;
            if (n.equals("")) return null;

            // The intention of this loop is to handle nested
            // elements.  If finding the element using Class.forName
            // fails, an attempt is made to find the element as an
            // enclosed element by trying fo find a prefix of the name
            // (dropping a trailing ".xyz") and looking for "xyz" as
            // an enclosed element.

            Deque<String> parts = new ArrayDeque<>();
            boolean again;
            do {
                again = false;
                try {
                    tmp = createMirror(Class.forName(n));
                } catch (ClassNotFoundException e) {
                    tmp = null;
                }

                if (tmp != null) {
                    if (parts.isEmpty()) {
                        return tmp;
                    }

                    tmp = findInner(tmp, parts);
                    if (tmp != null) {
                        return tmp;
                    }
                }

                int indx = n.lastIndexOf('.');
                if (indx > -1) {
                    parts.addFirst(n.substring(indx + 1));
                    n = n.substring(0, indx);
                    again = true;
                }
            } while (again);

            return null;
        }

        // Recursively finds enclosed type elements named as part.top() popping part and repeating
        private ReflectionTypeElement findInner(ReflectionTypeElement e, Deque<String> parts) {
            if (parts.isEmpty()) {
                return e;
            }

            String part = parts.removeFirst();
            List<ReflectionElement> enclosed = e.getEnclosedElements();
            for (ReflectionElement elm : enclosed) {
                if ((elm.getKind() == ElementKind.CLASS ||
                     elm.getKind() == ElementKind.INTERFACE ||
                     elm.getKind() == ElementKind.ENUM ||
                     elm.getKind() == ElementKind.ANNOTATION_TYPE)
                    && elm.getSimpleName().toString().equals(part)) {
                    ReflectionTypeElement t = findInner((ReflectionTypeElement)elm, parts);
                    if (t != null) {
                        return t;
                    }
                }
            }
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Map<? extends ReflectionExecutableElement, ? extends AnnotationValue>
            getElementValuesWithDefaults(AnnotationMirror a) {
            if (a instanceof CoreReflAnnotationMirror) {
                return ((CoreReflAnnotationMirror)a).getElementValues();
            } else {
                throw new IllegalArgumentException();
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getDocComment(Element e) {
            checkElement(e);
            return null; // As per the doc
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isDeprecated(Element e) {
            checkElement(e);
            return ((CoreReflElement)e).getSource().isAnnotationPresent(java.lang.Deprecated.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Name getBinaryName(TypeElement type) {
            checkElement(type);
            return StringName.instance(((CoreReflTypeElement)type)
                                       .getSource()
                                       .getName());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public ReflectionPackageElement getPackageOf(Element type) {
            checkElement(type);
            if (type instanceof ReflectionPackageElement) {
                return (ReflectionPackageElement)type;
            }

            Package p;
            if (type instanceof CoreReflTypeElement) {
                p = ((CoreReflTypeElement)type).getSource().getPackage();
            } else {
                CoreReflTypeElement enclosingTypeElement = (CoreReflTypeElement)getEnclosingTypeElement((ReflectionElement)type);
                p = enclosingTypeElement.getSource().getPackage();
            }

            return createMirror(p);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public List<? extends ReflectionElement> getAllMembers(TypeElement type) {
            checkElement(type);
            return getAllMembers((ReflectionTypeElement)type);
        }

        // Exercise for the reader: should this method, and similar
        // ones that specialize on the more specific argument types,
        // be addd to the public ReflectionElements API?
        public List<? extends ReflectionElement> getAllMembers(ReflectionTypeElement type) {
            return type.getAllMembers();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
            checkElement(e);
            AnnotatedElement ae = CoreReflElement.class.cast(e).getSource();
            Annotation[] annotations = ae.getAnnotations();
            int len = annotations.length;

            if (len > 0) {
                List<AnnotationMirror> res = new ArrayList<>(len);
                for (Annotation a : annotations) {
                    res.add(createMirror(a));
                }
                return Collections.unmodifiableList(res);
            } else {
                List<AnnotationMirror> ret = Collections.emptyList();
                return ret;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean hides(Element hider, Element hidden) {
            checkElement(hider);
            checkElement(hidden);

            // Names must be equal
            if (!hider.getSimpleName().equals(hidden.getSimpleName())) {
                return false;
            }

            // Hides isn't reflexive
            if (hider.equals(hidden)) {
                return false;
            }

            // Hider and hidden needs to be field, method or type
            // and fields hide fields, types hide types, methods hide methods
            // IE a Field doesn't hide a Methods etc
            ElementKind hiderKind = hider.getKind();
            ElementKind hiddenKind = hidden.getKind();
            if (hiderKind.isField() && !hiddenKind.isField()) {
                return false;
            } else if (hiderKind.isClass() &&
                       !(hiddenKind.isClass() || hiddenKind.isInterface())) {
                return false;
            } else if (hiderKind.isInterface() &&
                       !(hiddenKind.isClass() || hiddenKind.isInterface())) {
                return false;
            } else if (hiderKind == ElementKind.METHOD && hiddenKind != ElementKind.METHOD) {
                return false;
            } else if (!(hiderKind.isClass() ||
                         hiderKind.isInterface() ||
                         hiderKind.isField() ||
                         hiderKind == ElementKind.METHOD)) {
                return false;
            }

            Set<Modifier> hm = hidden.getModifiers();
            // jls 8.4.8.2 only static methods can hide methods
            if (hider.getKind() == ElementKind.METHOD) {
                if (!hider.getModifiers().contains(Modifier.STATIC)) {
                    return false; // hider not static
                } else if (!hm.contains(Modifier.STATIC)) { // we know it's a method
                    return false; // hidden not static
                }

                // For methods we also need to check parameter types
                Class<?>[] h1 = ((CoreReflMethodExecutableElement)hider).getSource().getParameterTypes();
                Class<?>[] h2 = ((CoreReflMethodExecutableElement)hidden).getSource().getParameterTypes();
                if (h1.length != h2.length) {
                    return false;
                }
                for (int i = 0; i < h1.length; i++) {
                    if (h1[i] != h2[i]) {
                        return false;
                    }
                }
            }

            // You can only hide visible elements
            if (hm.contains(Modifier.PRIVATE)) {
                return false; // hidden private, can't be hidden
            } else if ((!(hm.contains(Modifier.PUBLIC) || hm.contains(Modifier.PROTECTED))) && // not private, not (public or protected) IE package private
                       (!getPackageOf(hider).equals(getPackageOf(hidden)))) {
                return false; // hidden package private, and different packages, IE not visible
            }

            // Ok so now hider actually hides hidden if hider is
            // declared on a subtype of hidden.
            //
            // TODO: should this be a proper subtype or is that taken
            // care of by the reflexive check in the beginning?
            //
            TypeMirror hiderType = getEnclosingTypeElement((ReflectionElement)hider).asType();
            TypeMirror hiddenType = getEnclosingTypeElement((ReflectionElement)hidden).asType();

            return getTypes().isSubtype(hiderType, hiddenType);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public ReflectionTypeElement getEnclosingTypeElement(ReflectionElement e) {
            if (e.getKind() == ElementKind.PACKAGE) {
                return null;
            }

            if(e instanceof CoreReflTypeParameterElement) {
                ReflectionElement encElem = ((CoreReflTypeParameterElement)e).getEnclosingElement();
                if (encElem instanceof ReflectionTypeElement) {
                    return (ReflectionTypeElement)encElem;
                } else  {
                    return getEnclosingTypeElement(encElem);
                }
            }

            Class<?> encl = null;
            if (e instanceof CoreReflTypeElement) {
                encl = ((CoreReflTypeElement)e).getSource().getDeclaringClass();
            } else if (e instanceof CoreReflExecutableElement) {
                encl = (((CoreReflExecutableElement)e).getSource()).getDeclaringClass();
            } else if (e instanceof CoreReflFieldVariableElement) {
                encl = ((CoreReflFieldVariableElement)e).getSource().getDeclaringClass();
            } else if (e instanceof CoreReflParameterVariableElement) {
                encl = ((CoreReflParameterVariableElement)e).getSource().getDeclaringExecutable().getDeclaringClass();
            }

            return encl == null ? null : createMirror(encl);
        }

        /**
         *{@inheritDoc}
         *
         * Note that this implementation does not handle the situation
         * where A overrides B and B overrides C but A does not
         * directly override C. In this case, this implementation will
         * erroneously return false.
         */
        @Override
        public boolean overrides(ExecutableElement overrider, ExecutableElement overridden,
                                 TypeElement type) {
            checkElement(overrider);
            checkElement(overridden);
            checkElement(type);

            // TODO handle transitive overrides
            return overridesDirect(overrider, overridden, type);
        }

        private boolean overridesDirect(ExecutableElement overrider, ExecutableElement overridden,
                                         TypeElement type) {
            // Should we check that at least one of the types
            // overrider has is in fact a supertype of the TypeElement
            // 'type' supplied?

            CoreReflExecutableElement rider = (CoreReflExecutableElement)overrider;
            CoreReflExecutableElement ridden = (CoreReflExecutableElement)overridden;
            CoreReflTypeElement riderType = (CoreReflTypeElement)type;

            // Names must match, redundant - see subsignature below
            if (!rider.getSimpleName().equals(ridden.getSimpleName())) {
                return false;
            }

            // Constructors don't override
            // TODO: verify this fact
            if (rider.getKind() == ElementKind.CONSTRUCTOR ||
                ridden.getKind() == ElementKind.CONSTRUCTOR) {
                return false;
            }

            // Overridden must be visible to be overridden
            // TODO Fix transitive visibility/override
            Set<Modifier> rm = ridden.getModifiers();
            if (rm.contains(Modifier.PRIVATE)) {
                return false; // overridden private, can't be overridden
            } else if ((!(rm.contains(Modifier.PUBLIC) || rm.contains(Modifier.PROTECTED))) && // not private, not (public or protected) IE package private
                       (!getPackageOf(rider).equals(getPackageOf(ridden)))) {
                return false; // ridden package private, and different packages, IE not visible
            }

            // Static methods doesn't override
            if (rm.contains(Modifier.STATIC) ||
                rider.getModifiers().contains(Modifier.STATIC)) {
                return false;
            }

            // Declaring class of overrider must be a subclass of declaring class of overridden
            // except we use the parameter type as declaring class of overrider
            if (!getTypes().isSubtype(riderType.asType(), getEnclosingTypeElement(ridden).asType())) {
                return false;
            }

            // Now overrider overrides overridden if the signature of rider is a subsignature of ridden
            return getTypes().isSubsignature(rider.asType(), ridden.asType());
        }

        /**
         *{@inheritDoc}
         */
        @Override
        public String getConstantExpression(Object value) {
            return Constants.format(value);
        }

        // If CoreReflectionFactory were a proper part of the JDK, the
        // analogous functionality in javac could be reused.
        private static class Constants {
            /**
             * Returns a string representation of a constant value (given in
             * standard wrapped representation), quoted and formatted as in
             * Java source.
             */
            public static String format(Object value) {
                if (value instanceof Byte)      return formatByte((Byte) value);
                if (value instanceof Short)     return formatShort((Short) value);
                if (value instanceof Long)      return formatLong((Long) value);
                if (value instanceof Float)     return formatFloat((Float) value);
                if (value instanceof Double)    return formatDouble((Double) value);
                if (value instanceof Character) return formatChar((Character) value);
                if (value instanceof String)    return formatString((String) value);
                if (value instanceof Integer ||
                    value instanceof Boolean)   return value.toString();
                else
                    throw new IllegalArgumentException("Argument is not a primitive type or a string; it " +
                                                       ((value == null) ?
                                                        "is a null value." :
                                                        "has class " +
                                                        value.getClass().getName()) + "." );
            }

            private static String formatByte(byte b) {
                return String.format("(byte)0x%02x", b);
            }

            private static String formatShort(short s) {
                return String.format("(short)%d", s);
            }

            private static String formatLong(long lng) {
                return lng + "L";
            }

            private static String formatFloat(float f) {
                if (Float.isNaN(f))
                    return "0.0f/0.0f";
                else if (Float.isInfinite(f))
                    return (f < 0) ? "-1.0f/0.0f" : "1.0f/0.0f";
                else
                    return f + "f";
            }

            private static String formatDouble(double d) {
                if (Double.isNaN(d))
                    return "0.0/0.0";
                else if (Double.isInfinite(d))
                    return (d < 0) ? "-1.0/0.0" : "1.0/0.0";
                else
                    return d + "";
            }

            private static String formatChar(char c) {
                return '\'' + quote(c) + '\'';
            }

            private static String formatString(String s) {
                return '"' + quote(s) + '"';
            }

            /**
             * Escapes each character in a string that has an escape sequence or
             * is non-printable ASCII.  Leaves non-ASCII characters alone.
             */
            private static String quote(String s) {
                StringBuilder buf = new StringBuilder();
                for (int i = 0; i < s.length(); i++) {
                    buf.append(quote(s.charAt(i)));
                }
                return buf.toString();
            }

            /**
             * Escapes a character if it has an escape sequence or is
             * non-printable ASCII.  Leaves ASCII characters alone.
             */
            private static String quote(char ch) {
                switch (ch) {
                case '\b':  return "\\b";
                case '\f':  return "\\f";
                case '\n':  return "\\n";
                case '\r':  return "\\r";
                case '\t':  return "\\t";
                case '\'':  return "\\'";
                case '\"':  return "\\\"";
                case '\\':  return "\\\\";
                default:
                    return (isPrintableAscii(ch))
                        ? String.valueOf(ch)
                        : String.format("\\u%04x", (int) ch);
                }
            }

            /**
             * Is a character printable ASCII?
             */
            private static boolean isPrintableAscii(char ch) {
                return ch >= ' ' && ch <= '~';
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void printElements(Writer w, Element... elements) {
            ElementVisitor<?, ?> printer = getPrinter(w);
            try {
                for (Element e : elements) {
                    checkElement(e);
                    printer.visit(e);
                }
            } finally {
                try {
                    w.flush();
                } catch (java.io.IOException e) { /* Ignore */;}
            }
        }

        private ElementVisitor<?, ?> getPrinter(Writer w) {
            // First try a reflective call into javac and if that
            // fails, fallback to a very simple toString-based
            // scanner.
            try {
                //reflective form of
                // return new com.sun.tools.javac.processing.PrintingProcessor.PrintingElementVisitor(w, getElements());
                Class<?> printProcClass =
                    ClassLoader.getSystemClassLoader().loadClass("com.sun.tools.javac.processing.PrintingProcessor$PrintingElementVisitor");
                Constructor<?> printProcCtor = printProcClass.getConstructor(Writer.class, Elements.class);
                return (ElementVisitor) printProcCtor.newInstance(w, getElements());
            } catch (ReflectiveOperationException | SecurityException e) {
                return new ElementScanner8<Writer, Void>(w){
                    @Override
                    public Writer scan(Element e, Void v) {
                        try {
                            DEFAULT_VALUE.append(e.toString());
                            DEFAULT_VALUE.append("\n");
                        } catch (java.io.IOException ioe) {
                            throw new RuntimeException(ioe);
                        }
                        return DEFAULT_VALUE;
                    }
                };
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Name getName(CharSequence cs) {
            return StringName.instance(cs.toString());
        }

        private void checkElement(Element e) {
            if(!(e instanceof CoreReflElement)) {
                throw new IllegalArgumentException();
            }
        }

        @Override
        public boolean isFunctionalInterface(TypeElement e) {
            throw new UnsupportedOperationException();
            // Update once this functionality is in core reflection
        }
    }

    private static class CoreReflTypes implements javax.lang.model.util.Types {
        private static Types instance = new CoreReflTypes();

        public static Types instance() {
            return instance;
        }

        // Private to suppress instantiation
        private CoreReflTypes() {}

        // Types methods
        @Override
        public Element asElement(TypeMirror t) {
            checkType(t);
            if (t instanceof javax.lang.model.type.TypeVariable) {
                ((javax.lang.model.type.TypeVariable)t).asElement();
            } else if (t instanceof DeclaredType) {
                return ((DeclaredType)t).asElement();
            }
            return null;
        }

        @Override
        public boolean isSameType(TypeMirror t1, TypeMirror t2) {
            if (t1.getKind() != t2.getKind()) {
                return false;
            }

            if (t1.getKind() == TypeKind.WILDCARD ||
                t2.getKind() == TypeKind.WILDCARD) {
                // Wildcards are not equal to any type
                return false;
            }

            if (t1 instanceof CoreReflDeclaredType &&
                t2 instanceof CoreReflDeclaredType) {
                return ((CoreReflDeclaredType)t1).isSameType((CoreReflDeclaredType)t2);
            } else if (t1 instanceof PrimitiveType &&
                       t2 instanceof PrimitiveType) {
                return t1.getKind() == t2.getKind();
            } else if (t1 instanceof NoType &&
                       t2 instanceof NoType) {
                return true;
            } else if (t1 instanceof NullType &&
                       t2 instanceof NullType) {
                return true;
            } else if (t1 instanceof ArrayType &&
                       t2 instanceof ArrayType) {
                return isSameType(((ArrayType)t1).getComponentType(), ((ArrayType)t2).getComponentType());
            }

            return false;
        }

        @Override
        public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
            checkType(t1);
            checkType(t2);

            if (isSameType(t1, t2)) {
                return true;
            } else if(t1.getKind() == TypeKind.NULL) {
                return true;
            }

            // This depth first traversal should terminate due to the ban on circular inheritance
            List<? extends TypeMirror> directSupertypes = directSupertypes(t1);
            if (directSupertypes.isEmpty()) {
                return false;
            }
            for (TypeMirror ti : directSupertypes) {
                if (isSameType(ti, t2) || isSubtype(ti, t2)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean contains(TypeMirror t1, TypeMirror t2) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
            checkType(m1);
            checkType(m2);

            ExecutableMethodType m0 = (ExecutableMethodType)m1;

            return m0.sameSignature((ExecutableMethodType)m2) || m0.sameSignature((ExecutableMethodType)erasure(m2));
        }

        @Override
        public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
            checkType(t);
            if (t instanceof ExecutableType ||
                t.getKind() == TypeKind.PACKAGE) {
                throw new IllegalArgumentException("You can't ask for direct supertypes for type: " + t);
            }
            return ((AbstractTypeMirror)t).directSuperTypes();
        }

        @Override
        public TypeMirror erasure(TypeMirror t) {
            checkType(t);
            return ((AbstractTypeMirror)t).erasure();
        }

        @Override
        public TypeElement boxedClass(javax.lang.model.type.PrimitiveType p) {
            throw new UnsupportedOperationException();
        }

        @Override
        public PrimitiveType unboxedType(TypeMirror t) {
            throw new UnsupportedOperationException();
        }

        @Override
        public TypeMirror capture(TypeMirror t) {
            checkType(t);
            return ((AbstractTypeMirror)t).capture();
        }

        @Override
        public PrimitiveType getPrimitiveType(TypeKind kind) {
            return PrimitiveType.instance(kind);
        }

        @Override
        public NullType getNullType() {
            return CoreReflNullType.getInstance();
        }

        @Override
        public javax.lang.model.type.NoType getNoType(TypeKind kind) {
            if (kind == TypeKind.NONE) {
                return NoType.getNoneInstance();
            } else if (kind == TypeKind.VOID) {
                return NoType.getVoidInstance();
            } else {
                throw new IllegalArgumentException("No NoType of kind: " + kind);
            }
        }

        @Override
        public ArrayType getArrayType(TypeMirror componentType) {
            throw new UnsupportedOperationException();
        }

        @Override
        public javax.lang.model.type.WildcardType getWildcardType(TypeMirror extendsBound,
                                                                  TypeMirror superBound) {
            throw new UnsupportedOperationException();
        }

        @Override
        public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
            throw new UnsupportedOperationException();
        }

        @Override
        public javax.lang.model.type.DeclaredType getDeclaredType(javax.lang.model.type.DeclaredType containing,
                                                                  TypeElement typeElem,
                                                                  TypeMirror... typeArgs) {
            throw new UnsupportedOperationException();
        }

        @Override
        public TypeMirror asMemberOf(javax.lang.model.type.DeclaredType containing, Element element) {
            throw new UnsupportedOperationException();
        }

        private void checkType(TypeMirror t) {
            if (!(t instanceof AbstractTypeMirror)) {
                throw new IllegalArgumentException("This Types implementation can only operate on CoreReflectionFactory type classes");
            }
        }
    }

    private abstract static class CoreReflDeclaredType extends AbstractTypeMirror
        implements javax.lang.model.type.DeclaredType {
        private Class<?> source = null;

        private CoreReflDeclaredType(Class<?> source) {
            super(TypeKind.DECLARED);
            this.source = source;
        }

        static DeclaredType instance(Class<?> source, Type genericSource) {
            if (genericSource instanceof ParameterizedType) {
                return new ParameterizedDeclaredType(source, (ParameterizedType)genericSource);
            } else if (genericSource instanceof Class) { // This happens when a field has a raw type
                if (!source.equals(genericSource)) {
                    throw new IllegalArgumentException("Don't know how to handle this");
                }
                return instance(source);
            }
            throw new IllegalArgumentException("Don't know how to create a declared type from: " +
                                               source +
                                               " and genericSource " +
                                               genericSource);
        }

        static DeclaredType instance(Class<?> source) {
            return new RawDeclaredType(source);
        }

        protected Class<?> getSource() {
            return source;
        }

        @Override
        public Element asElement() {
            return CoreReflectionFactory.createMirror(getSource());
        }

        abstract boolean isSameType(DeclaredType other);

        @Override
        TypeMirror capture() {
            return new CaptureDeclaredType(this);
        }

        private static class CaptureDeclaredType extends CoreReflDeclaredType {
            CoreReflDeclaredType cap;
            CaptureDeclaredType(CoreReflDeclaredType t) {
                super(t.source);
                this.cap = t;
            }

            @Override
            public List<? extends TypeMirror> getTypeArguments() {
                List<? extends TypeMirror> wrapped = cap.getTypeArguments();
                ArrayList<TypeMirror> res = new ArrayList<>(wrapped.size());
                res.ensureCapacity(wrapped.size());

                for (int i = 0; i < wrapped.size(); i++) {
                    TypeMirror t = wrapped.get(i);

                    if (t instanceof javax.lang.model.type.WildcardType) {
                        res.add(i, convert(t));
                    } else {
                        res.add(i, t);
                    }
                }
                return Collections.unmodifiableList(res);
            }

            private TypeMirror convert(TypeMirror t) {
                if (!(t instanceof javax.lang.model.type.WildcardType)) {
                    throw new IllegalArgumentException();
                } else {
                    javax.lang.model.type.WildcardType w = (javax.lang.model.type.WildcardType)t;
                    return TypeFactory.typeVariableInstance(w, w.getExtendsBound(), w.getSuperBound());
                }
            }

            @Override
            public TypeMirror getEnclosingType() {
                return cap.getEnclosingType();
            }

            @Override
            List<? extends TypeMirror> directSuperTypes() {
                return cap.directSuperTypes();
            }

            @Override
            boolean isSameType(DeclaredType other) {
                return other == this;
            }

            @Override
            public String toString() {
                return " CAPTURE of: " + cap.toString();
            }
        }

        private static class RawDeclaredType extends CoreReflDeclaredType
            implements Reifiable {
            private RawDeclaredType(Class<?> source) {
                super(source);
            }

            @Override
            public Class<?> getSource() {
                return super.getSource();
            }

            @Override
            public TypeMirror getEnclosingType() {
                Class<?> enclosing = getSource().getEnclosingClass();
                if (null == enclosing) {
                    return NoType.getNoneInstance();
                } else {
                    return TypeFactory.instance(enclosing);
                }
            }

            @Override
            public List<? extends TypeMirror> getTypeArguments() {
                return Collections.emptyList();
            }

            @Override
            List<? extends TypeMirror> directSuperTypes() {
                if (getSource().isEnum()) {
                    return enumSuper();
                }

                if (getSource() == java.lang.Object.class) {
                    return Collections.emptyList();
                }
                List<TypeMirror> res = new ArrayList<>();
                Type[] superInterfaces = getSource().getInterfaces();
                if (!getSource().isInterface()) {
                    res.add(TypeFactory.instance(getSource().getSuperclass()));
                } else if (superInterfaces.length == 0) {
                    // Interfaces that don't extend another interface
                    // have java.lang.Object as a direct supertype.
                    return Collections.unmodifiableList(Arrays.asList(TypeFactory.instance(java.lang.Object.class)));
                }

                for (Type t : superInterfaces) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            }

            private List<? extends TypeMirror> enumSuper() {
                Class<?> rawSuper = getSource().getSuperclass();
                Type[] actualArgs = ((ParameterizedTypeImpl)getSource().getGenericSuperclass()).getActualTypeArguments();

                // Reconsider this : assume the problem is making
                // Enum<MyEnum> rather than just a raw enum.
                return Collections.unmodifiableList(Arrays.asList(TypeFactory.instance(ParameterizedTypeImpl.make(rawSuper,
                                                                                                                  Arrays.copyOf(actualArgs,
                                                                                                                                actualArgs.length),
                                                                                                                  null))));
            }

            @Override
            boolean isSameType(DeclaredType other) {
                if (other instanceof RawDeclaredType) {
                    return Objects.equals(getSource(), ((RawDeclaredType)other).getSource());
                } else {
                    return false;
                }
            }

            @Override
            public String toString() {
                return getSource().toString();
            }
        }

        private static class ParameterizedDeclaredType extends CoreReflDeclaredType {
            private ParameterizedType genericSource = null;
            private ParameterizedDeclaredType(Class<?> source, ParameterizedType genericSource) {
                super(source);
                this.genericSource = genericSource;
            }

            @Override
            public TypeMirror getEnclosingType() {
                Type me = genericSource;
                Type owner = GenericTypes.getEnclosingType(me);
                if (owner == null) {
                    return NoType.getNoneInstance();
                }
                return TypeFactory.instance(owner);
            }

            @Override
            public List<? extends TypeMirror> getTypeArguments() {
                Type[] typeArgs = genericSource.getActualTypeArguments();

                int length = typeArgs.length;
                if (length == 0)
                    return Collections.emptyList();
                else {
                    List<TypeMirror> tmp = new ArrayList<>(length);
                    for (Type t : typeArgs) {
                        tmp.add(TypeFactory.instance(t));
                    }
                    return Collections.unmodifiableList(tmp);
                }
            }

            @Override
            List<? extends TypeMirror> directSuperTypes() {
                if (getSource() == java.lang.Object.class) {
                    return Collections.emptyList();
                }

                List<TypeMirror> res = new ArrayList<>();
                Type[] superInterfaces = getSource().getGenericInterfaces();
                if (!getSource().isInterface()) {
                    // Replace actual type arguments with our type arguments
                    res.add(TypeFactory.instance(substituteTypeArgs(getSource().getGenericSuperclass())));
                } else if (superInterfaces.length == 0) {
                    // Interfaces that don't extend another interface
                    // have java.lang.Object as a direct supertype, plus
                    // possibly the interface's raw type
                    res.add(TypeFactory.instance(java.lang.Object.class));
                }

                for (Type t : superInterfaces) {
                    res.add(TypeFactory.instance(substituteTypeArgs(t)));
                }

                res.add(TypeFactory.instance(getSource())); // Add raw type
                return Collections.unmodifiableList(res);
            }

            private Type substituteTypeArgs(Type type) {
                if (!(type instanceof ParameterizedType)) {
                    return type;
                }

                ParameterizedType target = (ParameterizedType)type;
                // Cast to get a Class instead of a plain type.
                Class<?> raw = ((ParameterizedTypeImpl)target).getRawType();
                Type[] actualArgs = genericSource.getActualTypeArguments();

                return  ParameterizedTypeImpl.make(raw, Arrays.copyOf(actualArgs, actualArgs.length), null);
            }

            @Override
            boolean isSameType(DeclaredType other) {
                if (other instanceof ParameterizedDeclaredType) {
                    return GenericTypes.isSameGenericType(genericSource,
                                                          ((ParameterizedDeclaredType)other).genericSource);
                } else {
                    return false;
                }
            }

            @Override
            public String toString() {
                return getKind().toString() + " " + genericSource.toString();
            }
        }

        /**
         * Implementing class for ParameterizedType interface.
         * Derived from sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
         */

        private static class ParameterizedTypeImpl implements ParameterizedType {
            private Type[] actualTypeArguments;
            private Class<?>  rawType;
            private Type   ownerType;

            private ParameterizedTypeImpl(Class<?> rawType,
                                          Type[] actualTypeArguments,
                                          Type ownerType) {
                this.actualTypeArguments = actualTypeArguments;
                this.rawType             = rawType;
                if (ownerType != null) {
                    this.ownerType = ownerType;
                } else {
                    this.ownerType = rawType.getDeclaringClass();
                }
                validateConstructorArguments();
            }

            private void validateConstructorArguments() {
                java.lang.reflect.TypeVariable/*<?>*/[] formals = rawType.getTypeParameters();
                // check correct arity of actual type args
                if (formals.length != actualTypeArguments.length){
                    throw new MalformedParameterizedTypeException();
                }
            }

            /**
             * Static factory. Given a (generic) class, actual type arguments
             * and an owner type, creates a parameterized type.
             * This class can be instantiated with a a raw type that does not
             * represent a generic type, provided the list of actual type
             * arguments is empty.
             * If the ownerType argument is null, the declaring class of the
             * raw type is used as the owner type.
             * <p> This method throws a MalformedParameterizedTypeException
             * under the following circumstances:
             * If the number of actual type arguments (i.e., the size of the
             * array {@code typeArgs}) does not correspond to the number of
             * formal type arguments.
             * If any of the actual type arguments is not an instance of the
             * bounds on the corresponding formal.
             * @param rawType the Class representing the generic type declaration being
             * instantiated
             * @param actualTypeArguments - a (possibly empty) array of types
             * representing the actual type arguments to the parameterized type
             * @param ownerType - the enclosing type, if known.
             * @return An instance of {@code ParameterizedType}
             * @throws MalformedParameterizedTypeException - if the instantiation
             * is invalid
             */
            public static ParameterizedTypeImpl make(Class<?> rawType,
                                                     Type[] actualTypeArguments,
                                                     Type ownerType) {
                return new ParameterizedTypeImpl(rawType, actualTypeArguments,
                                                 ownerType);
            }


            /**
             * Returns an array of {@code Type} objects representing the actual type
             * arguments to this type.
             *
             * <p>Note that in some cases, the returned array be empty. This can occur
             * if this type represents a non-parameterized type nested within
             * a parameterized type.
             *
             * @return an array of {@code Type} objects representing the actual type
             *     arguments to this type
             * @throws {@code TypeNotPresentException} if any of the
             *     actual type arguments refers to a non-existent type declaration
             * @throws {@code MalformedParameterizedTypeException} if any of the
             *     actual type parameters refer to a parameterized type that cannot
             *     be instantiated for any reason
             * @since 1.5
             */
            public Type[] getActualTypeArguments() {
                return actualTypeArguments.clone();
            }

            /**
             * Returns the {@code Type} object representing the class or interface
             * that declared this type.
             *
             * @return the {@code Type} object representing the class or interface
             *     that declared this type
             */
            public Class<?> getRawType() {
                return rawType;
            }


            /**
             * Returns a {@code Type} object representing the type that this type
             * is a member of.  For example, if this type is {@code O<T>.I},
             * return a representation of {@code O<T>}.
             *
             * <p>If this type is a top-level type, {@code null} is returned.
             *
             * @return a {@code Type} object representing the type that
             *     this type is a member of. If this type is a top-level type,
             *     {@code null} is returned
             */
            public Type getOwnerType() {
                return ownerType;
            }

            /*
             * From the JavaDoc for java.lang.reflect.ParameterizedType
             * "Instances of classes that implement this interface must
             * implement an equals() method that equates any two instances
             * that share the same generic type declaration and have equal
             * type parameters."
             */
            @Override
            public boolean equals(Object o) {
                if (o instanceof ParameterizedType) {
                    // Check that information is equivalent
                    ParameterizedType that = (ParameterizedType) o;

                    if (this == that)
                        return true;

                    Type thatOwner   = that.getOwnerType();
                    Type thatRawType = that.getRawType();

                    return Objects.equals(ownerType, thatOwner) &&
                        Objects.equals(rawType, thatRawType) &&
                        Arrays.equals(actualTypeArguments, // avoid clone
                                      that.getActualTypeArguments());
                } else
                    return false;
            }

            @Override
            public int hashCode() {
                return
                    Arrays.hashCode(actualTypeArguments) ^
                    Objects.hashCode(ownerType) ^
                    Objects.hashCode(rawType);
            }

            public String toString() {
                StringBuilder sb = new StringBuilder();

                if (ownerType != null) {
                    if (ownerType instanceof Class)
                        sb.append(((Class)ownerType).getName());
                    else
                        sb.append(ownerType.toString());

                    sb.append(".");

                    if (ownerType instanceof ParameterizedTypeImpl) {
                        // Find simple name of nested type by removing the
                        // shared prefix with owner.
                        sb.append(rawType.getName().replace( ((ParameterizedTypeImpl)ownerType).rawType.getName() + "$",
                                                             ""));
                    } else
                        sb.append(rawType.getName());
                } else
                    sb.append(rawType.getName());

                if (actualTypeArguments != null &&
                    actualTypeArguments.length > 0) {
                    sb.append("<");
                    boolean first = true;
                    for (Type t: actualTypeArguments) {
                        if (!first)
                            sb.append(", ");
                        if (t instanceof Class)
                            sb.append(((Class)t).getName());
                        else
                            sb.append(t.toString());
                        first = false;
                    }
                    sb.append(">");
                }

                return sb.toString();
            }
        }

    }

    private static class ErasedMethodType extends ExecutableMethodType implements javax.lang.model.type.ExecutableType {
        private final Method m;

        ErasedMethodType(Method m) {
            super(m);
            this.m = Objects.requireNonNull(m);
        }

        @Override
        public List<javax.lang.model.type.TypeVariable> getTypeVariables() {
            return Collections.emptyList();
        }

        @Override
        public List<? extends TypeMirror> getThrownTypes() {
            Class<?>[] exceptions = m.getExceptionTypes();
            int len = exceptions.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList(len);
                for (Class<?> t : exceptions) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                List<TypeMirror> ret = Collections.emptyList();
                return ret;
            }
        }

        @Override
        public List<? extends TypeMirror> getParameterTypes() {
            Class<?>[] params = m.getParameterTypes();
            int len = params.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList(len);
                for (Class<?> t : params) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                List<TypeMirror> ret = Collections.emptyList();
                return ret;
            }
        }

        @Override
        public TypeMirror getReturnType() {
            return TypeFactory.instance(m.getReturnType());
        }

        @Override
        TypeMirror erasure() {
            return this;
        }
    }

    private static class ErrorType extends AbstractTypeMirror implements javax.lang.model.type.ErrorType {
        private static ErrorType errorType = new ErrorType();

        public static ErrorType getErrorInstance() {
            return errorType;
        }

        private ErrorType() {
            super(TypeKind.ERROR);
        }

        @Override
        public List<? extends TypeMirror> getTypeArguments() {
            throw new UnsupportedOperationException();
        }

        @Override
        public TypeMirror getEnclosingType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Element asElement() {
            throw new UnsupportedOperationException();
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            throw new UnsupportedOperationException();
        }
    }

    private static class ExecutableMethodType extends AbstractTypeMirror
        implements javax.lang.model.type.ExecutableType {
        private final Method m;

        ExecutableMethodType(Method m) {
            super(TypeKind.EXECUTABLE);
            this.m = Objects.requireNonNull(m);
        }

        @Override
        public List<? extends TypeMirror> getThrownTypes() {
            Type[] exceptions = m.getGenericExceptionTypes();
            int len = exceptions.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList(len);
                for (Type t : exceptions) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                List<TypeMirror> ret = Collections.emptyList();
                return ret;
            }
        }

        @Override
        public List<javax.lang.model.type.TypeVariable> getTypeVariables() {
            java.lang.reflect.TypeVariable[] variables = m.getTypeParameters();
            int len = variables.length;

            if (len > 0) {
                List<javax.lang.model.type.TypeVariable> res = new ArrayList<>(len);
                for (java.lang.reflect.TypeVariable<?> t : variables) {
                    res.add(TypeFactory.typeVariableInstance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                return Collections.emptyList();
            }
        }

        @Override
        public TypeMirror getReturnType() {
            return TypeFactory.instance(m.getGenericReturnType());
        }

        @Override
        public List<? extends TypeMirror> getParameterTypes() {
            Type[] params = m.getGenericParameterTypes();
            int len = params.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList(len);
                for (Type t : params) {
                    res.add(TypeFactory.instance(t));
                }
                return Collections.unmodifiableList(res);
            } else {
                return Collections.emptyList();
            }
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            // Spec says we don't need this
            throw new UnsupportedOperationException();
        }

        @Override
        TypeMirror erasure() {
            return new ErasedMethodType(m);
        }

        @Override
        public TypeMirror getReceiverType() {
            throw new UnsupportedOperationException();
        }

        boolean sameSignature(ExecutableMethodType other){
            if (!m.getName().equals(other.m.getName())) {
                return false;
            }

            List<? extends TypeMirror> thisParams = getParameterTypes();
            List<? extends TypeMirror> otherParams = other.getParameterTypes();
            if (thisParams.size() != otherParams.size()) {
                return false;
            }
            for (int i = 0; i < thisParams.size(); i++) {
                if (!CoreReflTypes.instance().isSameType(thisParams.get(i), otherParams.get(i))) {
                    return false;
                }
            }
            return true;
        }
    }

    private static class GenericTypes {
        public static boolean isSameGenericType(Type t1, Type t2) {
            if (t1 instanceof Class) {
                return ((Class)t1).equals(t2);
            } else if (t1 instanceof ParameterizedType) {
                return ((ParameterizedType)t1).equals(t2);
            }
            throw new UnsupportedOperationException();
        }

        public static Type getEnclosingType(Type t1) {
            if (t1 instanceof Class) {
                return ((Class)t1).getEnclosingClass();
            } else if (t1 instanceof ParameterizedType) {
                return ((ParameterizedType)t1).getOwnerType();
            }
            throw new UnsupportedOperationException();
        }
    }

    private static class IntersectionDeclaredType extends AbstractTypeMirror
        implements javax.lang.model.type.DeclaredType {
        private Type[] sources = null;

        IntersectionDeclaredType(Type[] sources) {
            super(TypeKind.DECLARED);
            this.sources = Arrays.copyOf(Objects.requireNonNull(sources),
                                         sources.length);
        }

        @Override
        public TypeMirror getEnclosingType() {
            return NoType.getNoneInstance();
        }

        @Override
        public  Element asElement() {
            throw new UnsupportedOperationException();
        }

        @Override
        public List<? extends TypeMirror> getTypeArguments() {
            throw new UnsupportedOperationException();
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            int len = sources.length;

            if (len > 0) {
                List<TypeMirror> res = new ArrayList(len);
                for (Type c : sources) {
                    res.add(TypeFactory.instance(c));
                }
                return Collections.unmodifiableList(res);
            } else {
                return Collections.emptyList();
            }
        }
    }

    private static class ModelWildcardType extends AbstractTypeMirror
        implements javax.lang.model.type.WildcardType {
        private java.lang.reflect.WildcardType genericSource;

        ModelWildcardType(java.lang.reflect.WildcardType genericSource) {
            super(TypeKind.WILDCARD);
            this.genericSource = Objects.requireNonNull(genericSource);
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            // TODO Add support for this operation
            throw new UnsupportedOperationException();
        }

        @Override
        public TypeMirror getExtendsBound() {
            Type[] t = genericSource.getUpperBounds();

            if (t.length == 1) {
                if (t[0].equals(Object.class) && getSuperBound() != null) { // can't have both lower and upper explicit
                    return null;
                }
                return TypeFactory.instance(t[0]);
            }
            throw new UnsupportedOperationException(); // TODO: intersection type?
        }

        @Override
        public TypeMirror getSuperBound() {
            Type[] t = genericSource.getLowerBounds();

            if (t.length == 0) { // bound is null
                return null;
            } else if (t.length == 1) {
                return TypeFactory.instance(t[0]);
            }
            throw new UnsupportedOperationException(); // TODO: intersection type?
        }

        @Override
        public String toString() {
            return getKind() + " " + genericSource.toString();
        }
    }

    private static class NoType extends AbstractTypeMirror
        implements javax.lang.model.type.NoType {
        private static NoType noneType = new NoType(TypeKind.NONE, "none");
        private static NoType packageType = new NoType(TypeKind.PACKAGE, "package");
        private static NoType voidType = new NoType(TypeKind.VOID, "void");

        private String str;

        public static NoType getNoneInstance() {
            return noneType;
        }

        public static NoType getPackageInstance() {
            return packageType;
        }

        public static NoType getVoidInstance() {
            return voidType;
        }

        private NoType(TypeKind k, String str) {
            super(k);
            this.str = str;
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            // TODO We don't need this for the Package instance, how about the others?
            throw new UnsupportedOperationException();
        }

        @Override
        public String toString() {
            return str;
        }
    }

    private static class CoreReflNullType extends AbstractTypeMirror
        implements javax.lang.model.type.NullType {
        private static CoreReflNullType nullType = new CoreReflNullType();

        public static NullType getInstance() {
            return nullType;
        }

        private CoreReflNullType() {
            super(TypeKind.NULL);
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            // JLS 4.10.2 says:
            // "The direct supertypes of the null type are all reference types other than the null type itself."
            // TODO return null? an empty list? the error type? anyhow fix this
            throw new UnsupportedOperationException();
        }
    }

    private static interface Reifiable {
        Class<?> getSource();
    }

    private static class PrimitiveType extends AbstractTypeMirror
        implements javax.lang.model.type.PrimitiveType,
                   Reifiable {
        private Class<?> source;

        private static PrimitiveType booleanInstance = new PrimitiveType(TypeKind.BOOLEAN, boolean.class);
        private static PrimitiveType byteInstance =    new PrimitiveType(TypeKind.BYTE, byte.class);
        private static PrimitiveType charInstance =    new PrimitiveType(TypeKind.CHAR, char.class);
        private static PrimitiveType shortInstance =   new PrimitiveType(TypeKind.SHORT, short.class);
        private static PrimitiveType intInstance =     new PrimitiveType(TypeKind.INT, int.class);
        private static PrimitiveType longInstance =    new PrimitiveType(TypeKind.LONG, long.class);
        private static PrimitiveType floatInstance =   new PrimitiveType(TypeKind.FLOAT, float.class);
        private static PrimitiveType doubleInstance =  new PrimitiveType(TypeKind.DOUBLE, double.class);

        private PrimitiveType(TypeKind kind, Class<?> source) {
            super(kind);
            this.source = source;
        }

        @Override
        public Class<?> getSource() {
            return source;
        }

        static PrimitiveType instance(Class<?> c) {
            switch(c.getName()) {
            case "boolean":
                return booleanInstance;
            case "byte":
                return byteInstance;
            case "char":
                return charInstance;
            case "short":
                return shortInstance;
            case "int":
                return intInstance;
            case "long":
                return longInstance;
            case "float":
                return floatInstance;
            case "double":
                return doubleInstance;
            default:
                throw new IllegalArgumentException();
            }
        }

        static PrimitiveType instance(TypeKind k) {
            switch(k) {
            case BOOLEAN:
                return booleanInstance;
            case BYTE:
                return byteInstance;
            case CHAR:
                return charInstance;
            case SHORT:
                return shortInstance;
            case INT:
                return intInstance;
            case LONG:
                return longInstance;
            case FLOAT:
                return floatInstance;
            case DOUBLE:
                return doubleInstance;
            default:
                throw new IllegalArgumentException();
            }
        }

        @Override
        public String toString() {
            return source.getName();
        }

        //Types methods
        @Override
        List<? extends TypeMirror> directSuperTypes() {
            switch (getKind()) {
            case DOUBLE:
                return Collections.emptyList();
            case FLOAT:
                return Arrays.asList(doubleInstance);
            case LONG:
                return Arrays.asList(floatInstance);
            case INT:
                return Arrays.asList(longInstance);
            case CHAR:
                return Arrays.asList(intInstance);
            case SHORT:
                return Arrays.asList(intInstance);
            case BYTE:
                return Arrays.asList(shortInstance);
            default:
                return Collections.emptyList();
            }
        }
    }

    private static class TypeFactory {
        private TypeFactory() { }// no instances for you

        public static TypeMirror instance(Class<?> c) {
            if (c.isPrimitive()) {
                if (c.getName().equals("void")) {
                    return NoType.getVoidInstance();
                } else {
                    return PrimitiveType.instance(c);
                }
            } else if (c.isArray()) {
                return new CoreReflArrayType(c);
            } else if (c.isAnonymousClass() ||
                       c.isLocalClass() ||
                       c.isMemberClass() ||
                       c.isInterface() || // covers annotations
                       c.isEnum()) {
                return CoreReflDeclaredType.instance(c);
            } else { // plain old class ??
                return CoreReflDeclaredType.instance(c);
            }
        }

        public static TypeMirror instance(Type t) {
            if (t instanceof Class) {
                return instance((Class)t);
            } else if (t instanceof ParameterizedType) {
                ParameterizedType tmp = (ParameterizedType)t;
                Type raw = tmp.getRawType();
                if (!(raw instanceof Class)) {
                    throw new IllegalArgumentException(t + " " + raw );
                }
                return CoreReflDeclaredType.instance((Class)raw, tmp);
            } else if (t instanceof java.lang.reflect.WildcardType) {
                return new ModelWildcardType((java.lang.reflect.WildcardType)t);
            } else if (t instanceof java.lang.reflect.TypeVariable) {
            return new CoreReflTypeVariable((java.lang.reflect.TypeVariable)t);
            }
            throw new IllegalArgumentException("Don't know how to make instance from: " + t.getClass());
        }

        public static TypeMirror instance(Field f) {
            return CoreReflDeclaredType.instance(f.getType(), f.getGenericType());
        }

        public static ExecutableType instance(Method m) {
            return new ExecutableMethodType(m);
        }

        public static javax.lang.model.type.TypeVariable typeVariableInstance(java.lang.reflect.TypeVariable<?> v) {
            return new CoreReflTypeVariable(v);
        }

        public static javax.lang.model.type.TypeVariable typeVariableInstance(TypeMirror source,
                                                        TypeMirror upperBound,
                                                        TypeMirror lowerBound) {
            return new CaptureTypeVariable(source, upperBound, lowerBound);
        }
    }

    private static class CoreReflTypeVariable extends AbstractTypeMirror
        implements javax.lang.model.type.TypeVariable {
        private final java.lang.reflect.TypeVariable<?> source;
        private boolean isCapture = false;

        protected CoreReflTypeVariable(java.lang.reflect.TypeVariable<?> source) {
            super(TypeKind.TYPEVAR);
            Objects.requireNonNull(source);
            this.source = source;
        }

        @Override
        public TypeMirror getUpperBound() {
            return new IntersectionDeclaredType(source.getBounds());
        }

        @Override
        public TypeMirror getLowerBound() {
            return CoreReflTypes.instance().getNullType();
        }

        @Override
        public Element asElement() {
            return CoreReflectionFactory.createMirror(source);
        }

        @Override
        List<? extends TypeMirror> directSuperTypes() {
            return ((AbstractTypeMirror)getUpperBound()).directSuperTypes();
        }

        @Override
        public int hashCode() {
            return source.hashCode();
        }

        @Override
        public boolean equals(Object other) {
            if (other instanceof CoreReflTypeVariable) {
                return this.source.equals(((CoreReflTypeVariable)other).source);
            } else {
                return false;
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java CoreReflectionFactory.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.