class MimicCreator {

    public void mimicMethods(CtClass src, CtClass dst, MimicMode defaultMimicMode, MimicMethod[] mimicMethods) throws MimicException, CannotCompileException, NotFoundException {
        HashMap<String, MimicMode> mapNameToMimicMode = buildMimicModeMethodMap(mimicMethods);
        HashMap<String, String> mapNameToInsertionMethod = buildInsertionMethodMap(mimicMethods);

        for (final CtMethod method : src.getDeclaredMethods()) {
            log.fine("Mimic method " + method.getName());
            boolean destHasSameMethod = false;
            for (CtMethod methodInDest : dst.getDeclaredMethods()) {
                String signature = method.getSignature() + method.getName();
                String signatureInDest = methodInDest.getSignature() + methodInDest.getName();
                if (signatureInDest.equals(signature)) {
                    destHasSameMethod = true;
                    log.fine("Forwarding " + method.getName());
                    String key = this.key == null ? "" : (this.key + "_");
                    final String copiedMethodName = "_copy_" + key + method.getName();
                    dst.addMethod(CtNewMethod.copy(method, copiedMethodName, dst, null));

                    CtMethod insertionMethod = null;
                    MimicMode mimicMode = defaultMimicMode;
                    if (mapNameToMimicMode.containsKey(method.getName())) {
                        mimicMode = mapNameToMimicMode.get(method.getName());
                        String insertionMethodName = mapNameToInsertionMethod.get(method.getName());
                        insertionMethod = findMethod(dst, insertionMethod, insertionMethodName);
                    }
                    log.fine("Mimic mode " + mimicMode);
                    switch (mimicMode) {
                        case AT_BEGINNING:
                            methodInDest.insertBefore(createInvocation(methodInDest, copiedMethodName));
                            break;
                        case BEFORE_RETURN:
                            String returnString = method.getReturnType() == null ? "" : "return ";
                            methodInDest.insertAfter(returnString + createInvocation(methodInDest, copiedMethodName));
                            break;
                        case BEFORE:
                        case AFTER:
                            ReplaceExprEditor editor = new ReplaceExprEditor(copiedMethodName, method, insertionMethod, mimicMode);
                            methodInDest.instrument(editor);
                            if (!editor.isSuccessful()) {
                                throw new MimicException("No replacement for method:" + method.getName() + " with insertion AFTER " + (insertionMethod == null ? "insertion method is null" : insertionMethod.getName()));
                            }
                            break;
                        case BEFORE_SUPER:
                        case AFTER_SUPER:
                        case REPLACE_SUPER:
                            ReplaceExprEditor editor2 = new ReplaceExprEditor(copiedMethodName, method, mimicMode);
                            methodInDest.instrument(editor2);
                            if (!editor2.isSuccessful()) {
                                throw new MimicException("No replacement for method:" + method.getName() + " with insertion REPLACE_SUPER ");
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
            if (!destHasSameMethod) {
                log.fine("Copying " + method.getName());
                dst.addMethod(CtNewMethod.copy(method, dst, null));
            }
        }
    }

    public  MimicCreator();
    public  MimicCreator(String key);

}