// Path: egtest-annotations/src/main/java/com/vocalabs/egtest/annotation/EgLanguage.java
// public enum EgLanguage {
//     /** Use the default language specified at a higher level, typically JAVA. */
//     INHERIT,
//     /** Java, copied verbatim into test code. */
//     JAVA,
// 
//     /**
//      * Produce Groovy embedded in Java. May produce simple Java rather than embedding a Groovy interpreter for simple
//      * cases such as primitives.
//      */
//     GROOVY,
// 
//     /** Produce Kotlin source code within Kotlin source files. Can't be mixed with JAVA or GROOVY within a file. */
//     KOTLIN
// }
// 
// Path: egtest-processor/src/main/java/com/vocalabs/egtest/processor/AnnotationCollector.java
// public class AnnotationCollector {
//     private final Map<String, EgLanguage> languageForClassName = new HashMap<>();
//     private final Map<String, List<EgItem<?>>> itemsByClassName = new TreeMap<>();
//     private final MessageHandler messageHandler;
// 
//     public AnnotationCollector(MessageHandler messageHandler) {
//         this.messageHandler = messageHandler;
//     }
// 
//     public void add(EgItem<?> data) {
//         if (data instanceof DefaultLanguageReader.Item)
//             addDefaultLanguage(data);
//         else
//             addExample(data);
//     }
// 
//     private void addExample(EgItem<?> data) {
//         Element classEl = JavaModelUtil.topLevelClass(data.getElement());
//         if (classEl.getKind().equals(ElementKind.CLASS)) {
//             String name = className(classEl);
//             itemsByClassName.computeIfAbsent(name, x -> new ArrayList<>())
//                     .add(data);
//         }
//         else {
//             messageHandler.error("Container is not a class: "+data.getElement());
//         }
//     }
// 
//     private void addDefaultLanguage(EgItem<?> data) {
//         Element classEl = data.getElement();
//         if (classEl.getKind().equals(ElementKind.CLASS)) {
//             EgLanguage language = ((DefaultLanguageReader.Item)data).getAnnotation().value();
//             languageForClassName.put(className(classEl), language);
//         }
//         else {
//             messageHandler.error("Can't set default language except on Class: "+classEl);
//         }
//     }
// 
//     public static String className(Element classEl) {
//         DeclaredType dt = (DeclaredType) classEl.asType();
//         TypeElement te = (TypeElement) dt.asElement();
// 
//         if ( ! allowedNesting(te.getNestingKind())) {
//             throw new IllegalArgumentException("EgTest doesn't support local and anonymous classes: "+classEl);
//         }
//         return te.getQualifiedName().toString();
//     }
// 
//     private static boolean allowedNesting(NestingKind nk) {
//         switch (nk) {
//             case TOP_LEVEL: return true;
//             case MEMBER:    return true;
//             case LOCAL:     return false;
//             case ANONYMOUS: return false;
//             default:        return false;
//         }
//     }
// 
//     public EgLanguage languageForClassName(String className, EgLanguage defaultLanguage) {
//         return languageForClassName.getOrDefault(className, defaultLanguage);
//     }
// 
//     public Map<String,EgLanguage> getLanguageForClassName() {
//         return Collections.unmodifiableMap(languageForClassName);
//     }
// 
//     public Map<String, List<EgItem<?>>> getItemsByClassName() {
//         // The inner lists are mutable, but this at least discourages modification
//         return Collections.unmodifiableMap(itemsByClassName);
//     }
// 
//     public MessageHandler getMessageHandler() {
//         return messageHandler;
//     }
// }


import com.squareup.javapoet.TypeSpec;
import com.vocalabs.egtest.annotation.EgLanguage;
import com.vocalabs.egtest.processor.AnnotationCollector;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import java.util.EnumMap;
import java.util.Map;
            case  GROOVY: return new GroovyInjector(packageName);
            case INHERIT: throw new IllegalArgumentException("Bug: should have used the default language");
            default:     throw new IllegalArgumentException("Unexpected language "+language);
        }
    }

    private final EnumMap<EgLanguage, LanguageInjector> map = new EnumMap<>(EgLanguage.class);

    /**
     * Return an injector for this language. Each CodeInjector has at most one injector for each language.
     * @throws IllegalArgumentException for {@link EgLanguage#INHERIT}.
     */
    private LanguageInjector languageInjector(EgLanguage language) {
        return map.computeIfAbsent(language, this::createInjector);
    }

    /**
     * Calls {@link #languageInjector(EgLanguage)} for an annotation with the given annotated language (typically
     * INHERIT) and enclosing class.
     */
    public LanguageInjector languageInjector(EgLanguage annotatedLanguage, Element enclosingElement) {
        EgLanguage language = EgLanguage.INHERIT.equals(annotatedLanguage)
                ? classLanguage(enclosingElement)
                : annotatedLanguage;
        return  languageInjector(language);
    }

    private EgLanguage classLanguage(Element el) {
        if (el.equals(classElement))
            return defaultLanguage;  // Typical case
// Path: egtest-annotations/src/main/java/com/vocalabs/egtest/annotation/EgLanguage.java
// public enum EgLanguage {
//     /** Use the default language specified at a higher level, typically JAVA. */
//     INHERIT,
//     /** Java, copied verbatim into test code. */
//     JAVA,
// 
//     /**
//      * Produce Groovy embedded in Java. May produce simple Java rather than embedding a Groovy interpreter for simple
//      * cases such as primitives.
//      */
//     GROOVY,
// 
//     /** Produce Kotlin source code within Kotlin source files. Can't be mixed with JAVA or GROOVY within a file. */
//     KOTLIN
// }
// 
// Path: egtest-processor/src/main/java/com/vocalabs/egtest/processor/AnnotationCollector.java
// public class AnnotationCollector {
//     private final Map<String, EgLanguage> languageForClassName = new HashMap<>();
//     private final Map<String, List<EgItem<?>>> itemsByClassName = new TreeMap<>();
//     private final MessageHandler messageHandler;
// 
//     public AnnotationCollector(MessageHandler messageHandler) {
//         this.messageHandler = messageHandler;
//     }
// 
//     public void add(EgItem<?> data) {
//         if (data instanceof DefaultLanguageReader.Item)
//             addDefaultLanguage(data);
//         else
//             addExample(data);
//     }
// 
//     private void addExample(EgItem<?> data) {
//         Element classEl = JavaModelUtil.topLevelClass(data.getElement());
//         if (classEl.getKind().equals(ElementKind.CLASS)) {
//             String name = className(classEl);
//             itemsByClassName.computeIfAbsent(name, x -> new ArrayList<>())
//                     .add(data);
//         }
//         else {
//             messageHandler.error("Container is not a class: "+data.getElement());
//         }
//     }
// 
//     private void addDefaultLanguage(EgItem<?> data) {
//         Element classEl = data.getElement();
//         if (classEl.getKind().equals(ElementKind.CLASS)) {
//             EgLanguage language = ((DefaultLanguageReader.Item)data).getAnnotation().value();
//             languageForClassName.put(className(classEl), language);
//         }
//         else {
//             messageHandler.error("Can't set default language except on Class: "+classEl);
//         }
//     }
// 
//     public static String className(Element classEl) {
//         DeclaredType dt = (DeclaredType) classEl.asType();
//         TypeElement te = (TypeElement) dt.asElement();
// 
//         if ( ! allowedNesting(te.getNestingKind())) {
//             throw new IllegalArgumentException("EgTest doesn't support local and anonymous classes: "+classEl);
//         }
//         return te.getQualifiedName().toString();
//     }
// 
//     private static boolean allowedNesting(NestingKind nk) {
//         switch (nk) {
//             case TOP_LEVEL: return true;
//             case MEMBER:    return true;
//             case LOCAL:     return false;
//             case ANONYMOUS: return false;
//             default:        return false;
//         }
//     }
// 
//     public EgLanguage languageForClassName(String className, EgLanguage defaultLanguage) {
//         return languageForClassName.getOrDefault(className, defaultLanguage);
//     }
// 
//     public Map<String,EgLanguage> getLanguageForClassName() {
//         return Collections.unmodifiableMap(languageForClassName);
//     }
// 
//     public Map<String, List<EgItem<?>>> getItemsByClassName() {
//         // The inner lists are mutable, but this at least discourages modification
//         return Collections.unmodifiableMap(itemsByClassName);
//     }
// 
//     public MessageHandler getMessageHandler() {
//         return messageHandler;
//     }
// }


// Path: egtest-processor/src/main/java/com/vocalabs/egtest/writer/junit/CodeInjector.java
import com.squareup.javapoet.TypeSpec;
import com.vocalabs.egtest.annotation.EgLanguage;
import com.vocalabs.egtest.processor.AnnotationCollector;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import java.util.EnumMap;
import java.util.Map;

            case  GROOVY: return new GroovyInjector(packageName);
            case INHERIT: throw new IllegalArgumentException("Bug: should have used the default language");
            default:     throw new IllegalArgumentException("Unexpected language "+language);
        }
    }

    private final EnumMap<EgLanguage, LanguageInjector> map = new EnumMap<>(EgLanguage.class);

    /**
     * Return an injector for this language. Each CodeInjector has at most one injector for each language.
     * @throws IllegalArgumentException for {@link EgLanguage#INHERIT}.
     */
    private LanguageInjector languageInjector(EgLanguage language) {
        return map.computeIfAbsent(language, this::createInjector);
    }

    /**
     * Calls {@link #languageInjector(EgLanguage)} for an annotation with the given annotated language (typically
     * INHERIT) and enclosing class.
     */
    public LanguageInjector languageInjector(EgLanguage annotatedLanguage, Element enclosingElement) {
        EgLanguage language = EgLanguage.INHERIT.equals(annotatedLanguage)
                ? classLanguage(enclosingElement)
                : annotatedLanguage;
        return  languageInjector(language);
    }

    private EgLanguage classLanguage(Element el) {
        if (el.equals(classElement))
            return defaultLanguage;  // Typical case
        EgLanguage lang = languageForClassName.get(AnnotationCollector.className(el));