private void autoInjectGetSet(CtClass ctClass) throws Exception {

    // hibernate 可能需要 setter/getter 方法,好吧 我们为它添加这些方法

    for (CtField ctField : ctClass.getDeclaredFields()) {
      if (isFinal(ctField) || isStatic(ctField) || ctField.hasAnnotation(Validate.class)) continue;
      // Property name
      String propertyName =
          ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
      String getter = "get" + propertyName;
      String setter = "set" + propertyName;

      try {
        CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
        if (ctMethod.getParameterTypes().length > 0 || Modifier.isStatic(ctMethod.getModifiers())) {
          throw new NotFoundException("it's not a getter !");
        }
      } catch (NotFoundException noGetter) {

        String code =
            "public "
                + ctField.getType().getName()
                + " "
                + getter
                + "() { return this."
                + ctField.getName()
                + "; }";
        CtMethod getMethod = CtMethod.make(code, ctClass);
        getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC);
        ctClass.addMethod(getMethod);
      }

      try {
        CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
        if (ctMethod.getParameterTypes().length != 1
            || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
            || Modifier.isStatic(ctMethod.getModifiers())) {
          throw new NotFoundException("it's not a setter !");
        }
      } catch (NotFoundException noSetter) {
        CtMethod setMethod =
            CtMethod.make(
                "public void "
                    + setter
                    + "("
                    + ctField.getType().getName()
                    + " value) { this."
                    + ctField.getName()
                    + " = value; }",
                ctClass);
        setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC);
        ctClass.addMethod(setMethod);
      }
    }
    ctClass.defrost();
  }
  public static void patch(CtClass c, IPatcher patcher) throws Exception {
    treatClassToPatch(c);

    if (patcher != null) {
      patcher.initClass(c);
    }

    for (CtMethod m : c.getDeclaredMethods()) {
      boolean wasAbstract = false;
      String newBody = null;
      if (Modifier.isAbstract(m.getModifiers())) {
        m.setModifiers(m.getModifiers() - Modifier.ABSTRACT);
        wasAbstract = true;
      }
      if (patcher != null) {
        newBody = patcher.getNewBody(m);
      }
      if (newBody != null) {
        if (newBody.startsWith(AutomaticPatcher.INSERT_BEFORE)) {
          GwtPatcherUtils.insertBefore(
              m, newBody.substring(AutomaticPatcher.INSERT_BEFORE.length()));
        } else if (newBody.startsWith(AutomaticPatcher.INSERT_AFTER)) {
          GwtPatcherUtils.insertAfter(m, newBody.substring(AutomaticPatcher.INSERT_AFTER.length()));
        } else {
          GwtPatcherUtils.replaceImplementation(m, newBody);
        }
      } else if (wasAbstract) {
        if (patcher != null) {
          m.setBody(
              "{ throw new "
                  + UnsupportedOperationException.class.getName()
                  + "(\"Abstract method '"
                  + c.getSimpleName()
                  + "."
                  + m.getName()
                  + "()' is not patched by "
                  + patcher.getClass().getName()
                  + "\"); }");
        } else {
          m.setBody(
              "{ throw new "
                  + UnsupportedOperationException.class.getName()
                  + "(\"Abstract method '"
                  + c.getSimpleName()
                  + "."
                  + m.getName()
                  + "()' is not patched by any declared "
                  + IPatcher.class.getSimpleName()
                  + " instance\"); }");
        }
      }
    }

    if (patcher != null) {
      patcher.finalizeClass(c);
    }
  }
  private Set<CtMethod> getPatchMethods(Set<CtClass> patchClasses) {
    Set<CtMethod> result = new HashSet<CtMethod>();

    // add all @PatchMethod found in a temporary map
    Map<String, List<CtMethod>> temp = new HashMap<String, List<CtMethod>>();
    for (CtClass patchClass : patchClasses) {
      for (CtMethod ctMethod : patchClass.getDeclaredMethods()) {
        if (ctMethod.hasAnnotation(PatchMethod.class)) {
          if (!Modifier.isStatic(ctMethod.getModifiers())) {
            throw new GwtTestPatchException(
                "@"
                    + PatchMethod.class.getName()
                    + " has to be static : '"
                    + ctMethod.getLongName()
                    + "'");
          }
          String nameAndSignature =
              ctMethod.getName() + Descriptor.toString(ctMethod.getSignature());
          List<CtMethod> correspondingMethods = temp.get(nameAndSignature);

          if (correspondingMethods == null) {
            correspondingMethods = new ArrayList<CtMethod>();
            temp.put(nameAndSignature, correspondingMethods);
          }

          correspondingMethods.add(ctMethod);
        }
      }
    }

    // for each @PatchMethod with the same signature, filter to get one with
    // override=true
    for (Map.Entry<String, List<CtMethod>> entry : temp.entrySet()) {
      CtMethod methodToUse = getMethodToUse(entry.getValue(), PatchMethod.class);
      methodToUse.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
      result.add(methodToUse);
    }

    return result;
  }
  private CtMethod getInitMethod(Set<CtClass> patchClasses) {
    List<CtMethod> initMethods = new ArrayList<CtMethod>();

    for (CtClass patchClass : patchClasses) {
      for (CtMethod ctMethod : patchClass.getDeclaredMethods()) {
        if (ctMethod.hasAnnotation(InitMethod.class)) {
          if (!Modifier.isStatic(ctMethod.getModifiers())) {
            throw new GwtTestPatchException(
                "@"
                    + InitMethod.class.getName()
                    + " has to be static : '"
                    + ctMethod.getLongName()
                    + "'");
          }
          try {
            if (ctMethod.getParameterTypes().length != 1
                || ctMethod.getParameterTypes()[0] != GwtClassPool.getCtClass(CtClass.class)) {
              throw new GwtTestPatchException(
                  "@"
                      + InitMethod.class.getName()
                      + " method must have one and only one parameter of type '"
                      + CtClass.class.getName()
                      + "'");
            }
          } catch (NotFoundException e) {
            // should never happen
            throw new GwtTestPatchException(e);
          }
          initMethods.add(ctMethod);
        }
      }
    }

    CtMethod initMethod = getMethodToUse(initMethods, InitMethod.class);
    if (initMethod != null) {
      initMethod.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
    }
    return initMethod;
  }
 private static void removeNativeModifier(CtMethod m) throws Exception {
   if (Modifier.isNative(m.getModifiers())) {
     m.setModifiers(m.getModifiers() - Modifier.NATIVE);
   }
 }
  @Override
  public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {

    final CtClass ctClass = makeClass(applicationClass);
    if (ctClass.isInterface()) {
      return;
    }
    if (ctClass.getName().endsWith(".package")) {
      return;
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor && !ctClass.isInterface()) {
        CtConstructor defaultConstructor =
            CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    if (isScala(applicationClass)) {
      // Temporary hack for Scala. Done.
      applicationClass.enhancedByteCode = ctClass.toBytecode();
      ctClass.defrost();
      return;
    }

    for (CtField ctField : ctClass.getDeclaredFields()) {
      try {

        if (isProperty(ctField)) {

          // Property name
          String propertyName =
              ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
          String getter = "get" + propertyName;
          String setter = "set" + propertyName;

          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
            if (ctMethod.getParameterTypes().length > 0
                || Modifier.isStatic(ctMethod.getModifiers())) {
              throw new NotFoundException("it's not a getter !");
            }
          } catch (NotFoundException noGetter) {

            // Créé le getter
            String code =
                "public "
                    + ctField.getType().getName()
                    + " "
                    + getter
                    + "() { return this."
                    + ctField.getName()
                    + "; }";
            CtMethod getMethod = CtMethod.make(code, ctClass);
            getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC);
            ctClass.addMethod(getMethod);
          }

          if (!isFinal(ctField)) {
            try {
              CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
              if (ctMethod.getParameterTypes().length != 1
                  || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
                  || Modifier.isStatic(ctMethod.getModifiers())) {
                throw new NotFoundException("it's not a setter !");
              }
            } catch (NotFoundException noSetter) {
              // Créé le setter
              CtMethod setMethod =
                  CtMethod.make(
                      "public void "
                          + setter
                          + "("
                          + ctField.getType().getName()
                          + " value) { this."
                          + ctField.getName()
                          + " = value; }",
                      ctClass);
              setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC);
              ctClass.addMethod(setMethod);
              createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class);
            }
          }
        }

      } catch (Exception e) {
        Logger.error(e, "Error in PropertiesEnhancer");
        throw new UnexpectedException("Error in PropertiesEnhancer", e);
      }
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor) {
        CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    // Intercept all fields access
    for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) {
      ctMethod.instrument(
          new ExprEditor() {

            @Override
            public void edit(FieldAccess fieldAccess) throws CannotCompileException {
              try {

                // Acces à une property ?
                if (isProperty(fieldAccess.getField())) {

                  // TODO : vérifier que c'est bien un champ d'une classe de l'application
                  // (fieldAccess.getClassName())

                  // Si c'est un getter ou un setter
                  String propertyName = null;
                  if (fieldAccess
                          .getField()
                          .getDeclaringClass()
                          .equals(ctMethod.getDeclaringClass())
                      || ctMethod
                          .getDeclaringClass()
                          .subclassOf(fieldAccess.getField().getDeclaringClass())) {
                    if ((ctMethod.getName().startsWith("get")
                            || (!isFinal(fieldAccess.getField())
                                && ctMethod.getName().startsWith("set")))
                        && ctMethod.getName().length() > 3) {
                      propertyName = ctMethod.getName().substring(3);
                      propertyName =
                          propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
                    }
                  }

                  // On n'intercepte pas le getter de sa propre property
                  if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) {

                    String invocationPoint =
                        ctClass.getName()
                            + "."
                            + ctMethod.getName()
                            + ", line "
                            + fieldAccess.getLineNumber();

                    if (fieldAccess.isReader()) {

                      // Réécris l'accés en lecture à la property
                      fieldAccess.replace(
                          "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");

                    } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) {

                      // Réécris l'accés en ecriture à la property
                      fieldAccess.replace(
                          "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", "
                              + fieldAccess.getField().getType().getName()
                              + ".class, $1, \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");
                    }
                  }
                }

              } catch (Exception e) {
                throw new UnexpectedException("Error in PropertiesEnhancer", e);
              }
            }
          });
    }

    // Done.
    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
  /**
   * Creates a map of concrete json request handler invokers keyed by <b><code>
   * &lt;service-name&gt;/&lt;op-name&gt;</code></b>.
   *
   * @param handlerInstance The request handler instance to generate invokers for
   * @return the map of generated invokers
   */
  public static Map<String, Map<String, AbstractJSONRequestHandlerInvoker>> createInvokers(
      Object handlerInstance) {
    if (handlerInstance == null)
      throw new IllegalArgumentException("The passed handlerInstance was null");
    Map<String, AbstractJSONRequestHandlerInvoker> subInvokerMap =
        new HashMap<String, AbstractJSONRequestHandlerInvoker>();
    Map<String, Map<String, AbstractJSONRequestHandlerInvoker>> invokerMap =
        invokerCache.get(handlerInstance.getClass());
    if (invokerMap != null) {
      LOG.info("Found Cached Invokers for [{}]", handlerInstance.getClass().getName());
      return invokerMap;
    }
    invokerMap = new HashMap<String, Map<String, AbstractJSONRequestHandlerInvoker>>(1);

    LOG.info("Generating Invokers for [{}]", handlerInstance.getClass().getName());
    JSONRequestService svc = handlerInstance.getClass().getAnnotation(JSONRequestService.class);
    final String invokerServiceKey = svc.name();
    final String invokerServiceDescription = svc.description();

    invokerMap.put(invokerServiceKey, subInvokerMap);

    ClassPool cp = new ClassPool();
    cp.appendClassPath(new ClassClassPath(handlerInstance.getClass()));
    cp.appendClassPath(new ClassClassPath(AbstractJSONRequestHandlerInvoker.class));
    cp.importPackage(handlerInstance.getClass().getPackage().getName());
    Set<ClassLoader> classPathsAdded = new HashSet<ClassLoader>();
    Set<String> packagesImported = new HashSet<String>();
    try {
      final CtClass jsonRequestCtClass = cp.get(JSONRequest.class.getName());
      final CtClass parent = cp.get(AbstractJSONRequestHandlerInvoker.class.getName());
      CtClass targetClass = cp.get(handlerInstance.getClass().getName());
      Collection<Method> methods = getTargetMethods(handlerInstance.getClass());
      for (Method m : methods) {
        final JSONRequestHandler jsonHandler = m.getAnnotation(JSONRequestHandler.class);
        final String opName = jsonHandler.name();
        final String opDescription = jsonHandler.description();
        final RequestType opType = jsonHandler.type();

        int targetMethodHashCode = m.toGenericString().hashCode();
        final String className =
            String.format(
                "%s-%s%s-%s-%s",
                handlerInstance.getClass().getName(),
                invokerServiceKey,
                opName,
                "ServiceInvoker",
                targetMethodHashCode);

        final CtClass invokerClass = cp.makeClass(className, parent);
        CtField ctf = new CtField(targetClass, "typedTarget", invokerClass);
        ctf.setModifiers(ctf.getModifiers() | Modifier.FINAL);
        invokerClass.addField(ctf);
        for (CtConstructor parentCtor : parent.getConstructors()) {
          CtConstructor invokerCtor = CtNewConstructor.copy(parentCtor, invokerClass, null);
          invokerCtor.setBody(
              "{ super($$); typedTarget = (" + handlerInstance.getClass().getName() + ")$1; }");
          invokerClass.addConstructor(invokerCtor);
        }
        CtMethod invokerMethod =
            CtNewMethod.copy(
                parent.getDeclaredMethod("doInvoke", new CtClass[] {jsonRequestCtClass}),
                invokerClass,
                null);
        StringBuilder b = new StringBuilder("{this.typedTarget.").append(m.getName()).append("($1");
        final Class<?>[] ptypes = m.getParameterTypes();
        final int remainingParamCount = ptypes.length - 1;
        //				Set<Class<?>> classPathsAdded = new HashSet<Class<?>>();
        //				Set<String> packagesImported = new HashSet<String>();
        if (remainingParamCount > 0) {
          for (int i = 0; i < remainingParamCount; i++) {
            final Class<?> type = ptypes[i + 1];
            if (type.getName().contains("UniqueIdType")) {
              System.err.println("Comin Up....");
            }
            if (type.isPrimitive()) {
              b.append(", (").append(type.getName()).append(") null");
            } else {
              if (classPathsAdded.add(type.getClassLoader())) {
                cp.appendClassPath(new LoaderClassPath(type.getClassLoader()));
              }
              try {
                Package p = type.getPackage();
                if (p == null) {
                  if (type.isArray()) {
                    if (!type.getComponentType().isPrimitive()) {
                      p = type.getComponentType().getPackage();
                    }
                  }
                }
                if (type.isEnum()) {
                  final String f = type.getEnclosingClass().getName() + "." + type.getSimpleName();
                  b.append(", (").append(f).append(") null");
                  String pack = type.getEnclosingClass().getPackage().getName();
                  if (packagesImported.add(pack)) {
                    cp.importPackage(pack);
                  }
                  continue;
                }

                if (p != null) {
                  if (packagesImported.add(p.getName())) {
                    cp.importPackage(p.getName());
                  }
                }
              } catch (Exception ex) {
                ex.printStackTrace(System.err);
              }
              b.append(", (").append(type.getSimpleName()).append(") null");
            }
          }
        }

        b.append(");}");
        System.out.println("[" + m.getName() + "]: [" + b.toString() + "]");
        // invokerMethod.setBody("{this.typedTarget." + m.getName() + "($1);}");
        invokerMethod.setBody(b.toString());
        invokerMethod.setModifiers(invokerMethod.getModifiers() & ~Modifier.ABSTRACT);
        invokerClass.addMethod(invokerMethod);
        // invokerClass.writeFile(System.getProperty("java.io.tmpdir") + File.separator +
        // "jsoninvokers");
        Class<?> clazz =
            invokerClass.toClass(
                handlerInstance.getClass().getClassLoader(),
                handlerInstance.getClass().getProtectionDomain());
        Constructor<?> ctor =
            clazz.getDeclaredConstructor(
                Object.class,
                String.class,
                String.class,
                String.class,
                String.class,
                RequestType.class);
        AbstractJSONRequestHandlerInvoker invokerInstance =
            (AbstractJSONRequestHandlerInvoker)
                ctor.newInstance(
                    handlerInstance,
                    invokerServiceKey,
                    invokerServiceDescription,
                    opName,
                    opDescription,
                    opType);
        subInvokerMap.put(opName, invokerInstance);
      }
      invokerCache.put(handlerInstance.getClass(), invokerMap);
      return invokerMap;
    } catch (Exception ex) {
      LOG.error(
          "Failed to create RequestHandlerInvoker for [{}]",
          handlerInstance.getClass().getName(),
          ex);
      throw new RuntimeException(
          "Failed to create RequestHandlerInvoker [" + handlerInstance.getClass().getName() + "]",
          ex);
    }
  }