private Bytecode createAbstractMethodCode(ClassFile file, MethodInformation method)
     throws NotFoundException {
   if ((delegateField != null) && (!Modifier.isPrivate(delegateField.getModifiers()))) {
     // Call the corresponding method directly on the delegate
     Bytecode b = new Bytecode(file.getConstPool());
     int localVariables = MethodUtils.calculateMaxLocals(method.getMethod());
     b.setMaxLocals(localVariables);
     // load the delegate field
     b.addAload(0);
     b.addGetfield(
         file.getName(),
         delegateField.getName(),
         DescriptorUtils.classToStringRepresentation(delegateField.getType()));
     // load the parameters
     BytecodeUtils.loadParameters(b, method.getDescriptor());
     // invoke the delegate method
     b.addInvokeinterface(
         delegateField.getType().getName(),
         method.getName(),
         method.getDescriptor(),
         localVariables);
     // return the value if applicable
     BytecodeUtils.addReturnInstruction(b, method.getReturnType());
     return b;
   } else {
     if (!Modifier.isPrivate(method.getMethod().getModifiers())) {
       // if it is a parameter injection point we need to initalize the
       // injection point then handle the method with the method handler
       return createAbstractMethodHandler(file, method);
     } else {
       // if the delegate is private we need to use the method handler
       return createInterceptorBody(file, method);
     }
   }
 }
  @Override
  public void describeTo(StringBuilder stringBuilder) {
    ClosureUtil.SourceInfo sourceInfo = ClosureUtil.getSourceInfo(invoker.getClosure());
    if (sourceInfo == null) {
      ClassPool pool = ClassPool.getDefault();
      try {
        CtClass ctClass = pool.get(invoker.getClosure().getClass().getName());
        CtMethod ctMethod = ctClass.getDeclaredMethod("doCall");
        int lineNumber = ctMethod.getMethodInfo().getLineNumber(0);

        ClassFile classFile = ctClass.getClassFile();
        String sourceFile = classFile.getSourceFile();

        if (lineNumber != -1 && sourceFile != null) {
          stringBuilder
              .append("closure at line ")
              .append(lineNumber)
              .append(" of ")
              .append(sourceFile);
        } else {
          stringBuilder.append("closure ").append(invoker.getClosure().getClass().getName());
        }
      } catch (NotFoundException e) {
        stringBuilder.append(invoker.getClosure().getClass().getName());
      }
    } else {
      stringBuilder
          .append("closure at line ")
          .append(sourceInfo.getLineNumber())
          .append(" of ")
          .append(sourceInfo.getUri());
    }
  }
  private CodeCoverage(ClassLoader classLoader, MethodExclusion exclusion, String... classes)
      throws Exception {
    for (String clazz : classes) {
      Map<Tuple, Set<CodeLine>> map = new TreeMap<>();
      report.put(clazz, map);

      List<Tuple> mds = new ArrayList<>();
      descriptors.put(clazz, mds);

      InputStream is = classLoader.getResourceAsStream(clazz.replace(".", "/") + ".class");
      ClassFile classFile = getClassFile(clazz, is);

      List<MethodInfo> methods = classFile.getMethods();
      for (MethodInfo m : methods) {
        if (exclusion.exclude(classFile, m) == false) {
          String descriptor = m.getDescriptor();
          Tuple tuple = new Tuple(m.getName(), descriptor);
          map.put(tuple, new TreeSet<CodeLine>());
          mds.add(tuple);
        }
      }

      if (isAccessFlagSet(classFile.getAccessFlags(), AccessFlag.ANNOTATION)) {
        boolean hasAttributes = methods.size() > 0;
        annotations.put(clazz, hasAttributes);
        if (hasAttributes == false) {
          map.put(TYPE_USAGE, new TreeSet<CodeLine>());
          mds.add(TYPE_USAGE);
        }
      } else {
        fillSupers(classFile);
      }
    }
  }
  public void testJIRA256() throws Exception {
    // CtClass ec = sloader.get("test5.Entity");

    CtClass cc = sloader.makeClass("test5.JIRA256");
    ClassFile ccFile = cc.getClassFile();
    ConstPool constpool = ccFile.getConstPool();

    AnnotationsAttribute attr =
        new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
    javassist.bytecode.annotation.Annotation entityAnno =
        new javassist.bytecode.annotation.Annotation("test5.Entity", constpool);
    // = new javassist.bytecode.annotation.Annotation(constpool, ec);

    entityAnno.addMemberValue(
        "value", new javassist.bytecode.annotation.ArrayMemberValue(constpool));
    attr.addAnnotation(entityAnno);
    ccFile.addAttribute(attr);

    cc.writeFile();
    Object o = make(cc.getName());
    assertTrue(o.getClass().getName().equals("test5.JIRA256"));

    java.lang.annotation.Annotation[] annotations = o.getClass().getDeclaredAnnotations();
    assertEquals(1, annotations.length);
  }
    @Override
    public JElement allocate(File e) {

      JElement element;
      if (e.isDirectory()) {
        // Attempt to construct package
        element = assignPackage(e);
      } else {
        try {
          ClassFile f =
              JavassistClassFileHelper.constructClassFileForPath(
                  e.getPath(), new TFileInputStream(e));
          if (f.isInterface()) {
            return JInterface.getJInterface(f, ClasspathResolver.this);
          } else {
            return JClass.getJClass(f, ClasspathResolver.this);
          }
        } catch (FileNotFoundException e1) {
          throw new ClasspathAccessException("Couldnt find file", e1);
        } catch (IOException e1) {
          throw new ClasspathAccessException("Couldnt access file", e1);
        }
      }
      return element;
    }
  protected void buildClassLevelAnnotations(
      ClassFile classFile, ClassFile templateClassFile, ConstPool constantPool)
      throws NotFoundException {
    List<?> templateAttributes = templateClassFile.getAttributes();
    Iterator<?> templateItr = templateAttributes.iterator();
    Annotation templateEntityListeners = null;
    while (templateItr.hasNext()) {
      Object object = templateItr.next();
      if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
        AnnotationsAttribute attr = (AnnotationsAttribute) object;
        Annotation[] items = attr.getAnnotations();
        for (Annotation annotation : items) {
          String typeName = annotation.getTypeName();
          if (typeName.equals(EntityListeners.class.getName())) {
            templateEntityListeners = annotation;
          }
        }
      }
    }

    if (templateEntityListeners != null) {
      AnnotationsAttribute annotationsAttribute =
          new AnnotationsAttribute(constantPool, AnnotationsAttribute.visibleTag);
      List<?> attributes = classFile.getAttributes();
      Iterator<?> itr = attributes.iterator();
      Annotation existingEntityListeners = null;
      while (itr.hasNext()) {
        Object object = itr.next();
        if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
          AnnotationsAttribute attr = (AnnotationsAttribute) object;
          Annotation[] items = attr.getAnnotations();
          for (Annotation annotation : items) {
            String typeName = annotation.getTypeName();
            if (typeName.equals(EntityListeners.class.getName())) {
              logger.debug(
                  "Stripping out previous EntityListeners annotation at the class level - will merge into new EntityListeners");
              existingEntityListeners = annotation;
              continue;
            }
            annotationsAttribute.addAnnotation(annotation);
          }
          itr.remove();
        }
      }

      Annotation entityListeners =
          getEntityListeners(constantPool, existingEntityListeners, templateEntityListeners);
      annotationsAttribute.addAnnotation(entityListeners);

      classFile.addAttribute(annotationsAttribute);
    }
  }
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    // Lambdas and anonymous methods in Java 8 do not have a class name defined and so no
    // transformation should be done
    if (className == null) {
      return null;
    }

    String convertedClassName = className.replace('/', '.');

    if (isIgnored(convertedClassName)) {
      return null;
    }

    try {
      ClassFile classFile =
          new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
      List<?> attributes = classFile.getAttributes();
      Iterator<?> itr = attributes.iterator();
      while (itr.hasNext()) {
        Object object = itr.next();
        if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
          boolean containsTypeLevelAnnotation =
              containsTypeLevelPersistenceAnnotation(
                  ((AnnotationsAttribute) object).getAnnotations());
          if (containsTypeLevelAnnotation) {
            LOG.debug("Marking " + convertedClassName + " as transformed");
            transformedEntityClassNames.add(convertedClassName);
          } else {
            LOG.debug(
                "Marking "
                    + convertedClassName
                    + " as picked up by the transformer but not detected as an entity");
            transformedNonEntityClassNames.add(convertedClassName);
          }
        }
      }
    } catch (Exception e) {
      LOG.error(e);
      throw new IllegalClassFormatException(
          "Unable to mark " + convertedClassName + " as transformed.");
    }

    // We don't need to transform anything, so we'll return null
    return null;
  }
 /**
  * Client proxies are equal to other client proxies for the same bean.
  *
  * <p>The corresponding java code: <code>
  * return other instanceof MyProxyClassType.class
  * </code>
  */
 @Override
 protected MethodInfo generateEqualsMethod(ClassFile proxyClassType) {
   MethodInfo method =
       new MethodInfo(proxyClassType.getConstPool(), "equals", "(Ljava/lang/Object;)Z");
   method.setAccessFlags(AccessFlag.PUBLIC);
   Bytecode b = new Bytecode(proxyClassType.getConstPool());
   b.addAload(1);
   b.addInstanceof(proxyClassType.getName());
   b.add(Opcode.IRETURN);
   b.setMaxLocals(2);
   b.setMaxStack(1);
   method.setCodeAttribute(b.toCodeAttribute());
   return method;
 }
  @Override
  protected void addMethodsFromClass(ClassFile proxyClassType) {
    Method initializerMethod = null;
    int delegateParameterPosition = -1;
    if (delegateInjectionPoint instanceof ParameterInjectionPoint<?, ?>) {
      ParameterInjectionPoint<?, ?> parameterIP =
          (ParameterInjectionPoint<?, ?>) delegateInjectionPoint;
      if (parameterIP.getMember() instanceof Method) {
        initializerMethod = ((Method) parameterIP.getMember());
        delegateParameterPosition = parameterIP.getPosition();
      }
    }
    try {
      if (delegateParameterPosition >= 0) {
        addHandlerInitializerMethod(proxyClassType);
      }
      Class<?> cls = getBeanType();
      while (cls != null) {
        for (Method method : cls.getDeclaredMethods()) {
          MethodInformation methodInfo = new RuntimeMethodInformation(method);
          if (!method.getDeclaringClass().getName().equals("java.lang.Object")
              || method.getName().equals("toString")) {
            Bytecode methodBody = null;
            if ((delegateParameterPosition >= 0) && (initializerMethod.equals(method))) {
              methodBody =
                  createDelegateInitializerCode(
                      proxyClassType, methodInfo, delegateParameterPosition);
            }
            if (Modifier.isAbstract(method.getModifiers())) {
              methodBody = createAbstractMethodCode(proxyClassType, methodInfo);
            }

            if (methodBody != null) {
              log.trace("Adding method " + method);
              proxyClassType.addMethod(
                  MethodUtils.makeMethod(
                      methodInfo,
                      method.getExceptionTypes(),
                      methodBody,
                      proxyClassType.getConstPool()));
            }
          }
        }
        cls = cls.getSuperclass();
      }
    } catch (Exception e) {
      throw new WeldException(e);
    }
  }
 private void addHandlerInitializerMethod(ClassFile proxyClassType) throws Exception {
   StaticMethodInformation methodInfo =
       new StaticMethodInformation(
           "_initMH",
           new Class[] {Object.class},
           void.class,
           proxyClassType.getName(),
           Modifier.PRIVATE);
   proxyClassType.addMethod(
       MethodUtils.makeMethod(
           methodInfo,
           new Class[] {},
           createMethodHandlerInitializerBody(proxyClassType),
           proxyClassType.getConstPool()));
 }
 protected void fillSupers(ClassFile clazz) {
   String name = clazz.getName();
   List<String> list = supers.get(name);
   if (list == null) {
     list = new ArrayList<>();
     supers.put(name, list);
   }
   if (clazz.isInterface()) {
     Collections.addAll(list, clazz.getInterfaces());
   } else {
     String superclass = clazz.getSuperclass();
     if (superclass != null) {
       list.add(superclass);
     }
   }
 }
  @SuppressWarnings("unchecked")
  public void visit(ClassFile file) throws Exception {
    List<MethodInfo> methods = file.getMethods();
    for (MethodInfo mi : methods) {
      try {
        CodeAttribute codeAttribute = mi.getCodeAttribute();
        // ignore abstract methods
        if (codeAttribute == null) {
          continue;
        }

        final int maxStack = codeAttribute.getMaxStack();
        int modified = 0;

        for (MethodRewriter mr : rewriters) {
          modified += mr.visit(mi);
        }

        if (modified != 0) {
          codeAttribute.setMaxStack(maxStack + modified);
        }
      } catch (Exception e) {
        throw new IllegalStateException("Cannot rewrite method: " + mi, e);
      }
    }
  }
 /**
  * calls _initMH on the method handler and then stores the result in the methodHandler field as
  * then new methodHandler
  */
 private Bytecode createMethodHandlerInitializerBody(ClassFile proxyClassType) {
   Bytecode b = new Bytecode(proxyClassType.getConstPool(), 1, 2);
   b.add(Opcode.ALOAD_0);
   StaticMethodInformation methodInfo =
       new StaticMethodInformation(
           "_initMH", new Class[] {Object.class}, void.class, proxyClassType.getName());
   invokeMethodHandler(proxyClassType, b, methodInfo, false, DEFAULT_METHOD_RESOLVER);
   b.addCheckcast("javassist/util/proxy/MethodHandler");
   b.addPutfield(
       proxyClassType.getName(),
       "methodHandler",
       DescriptorUtils.classToStringRepresentation(MethodHandler.class));
   b.add(Opcode.RETURN);
   log.trace("Created MH initializer body for decorator proxy:  " + getBeanType());
   return b;
 }
 /** Client proxies use the following hashCode: <code>MyProxyName.class.hashCode()</code> */
 @Override
 protected MethodInfo generateHashCodeMethod(ClassFile proxyClassType) {
   MethodInfo method = new MethodInfo(proxyClassType.getConstPool(), "hashCode", "()I");
   method.setAccessFlags(AccessFlag.PUBLIC);
   Bytecode b = new Bytecode(proxyClassType.getConstPool());
   // MyProxyName.class.hashCode()
   int classLocation = proxyClassType.getConstPool().addClassInfo(proxyClassType.getName());
   b.addLdc(classLocation);
   // now we have the class object on top of the stack
   b.addInvokevirtual("java.lang.Object", "hashCode", "()I");
   // now we have the hashCode
   b.add(Opcode.IRETURN);
   b.setMaxLocals(1);
   b.setMaxStack(1);
   method.setCodeAttribute(b.toCodeAttribute());
   return method;
 }
  /**
   * add a bogus constructor call to a bytecode sequence so a constructor can pass bytecode
   * validation
   *
   * @param bytecode
   */
  public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) {
    MethodInfo constructorToCall = null;
    for (Object meth : file.getMethods()) {
      MethodInfo m = (MethodInfo) meth;
      if (m.getName().equals("<init>")) {
        constructorToCall = m;
        break;
      }
    }
    if (constructorToCall == null) {
      return false;
    }
    // push this onto the stack
    code.add(Bytecode.ALOAD_0);

    String[] params =
        DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor());
    for (String p : params) {
      // int char short boolean byte
      if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) {
        // push integer 0
        code.add(Opcode.ICONST_0);
      }
      // long
      else if (p.equals("J")) {
        code.add(Opcode.LCONST_0);
      }
      // double
      else if (p.equals("D")) {
        code.add(Opcode.DCONST_0);
      }
      // float
      else if (p.equals("F")) {
        code.add(Opcode.FCONST_0);
      }
      // arrays and reference types
      else {
        code.add(Opcode.ACONST_NULL);
      }
    }
    // all our args should be pushed onto the stack, call the constructor
    code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor());
    code.add(Opcode.RETURN);
    return true;
  }
 private Bytecode createAbstractMethodHandler(ClassFile file, MethodInformation methodInfo) {
   // this is slightly different to a normal method handler call, as we pass
   // in a TargetInstanceBytecodeMethodResolver. This resolver uses the
   // method handler to call getTargetClass to get the correct class type to
   // resolve the method with, and then resolves this method
   Bytecode b = new Bytecode(file.getConstPool());
   invokeMethodHandler(file, b, methodInfo, true, TargetInstanceBytecodeMethodResolver.INSTANCE);
   return b;
 }
  public BaseClassData(ClassFile file, ClassLoader loader, boolean replaceable) {
    className = file.getName();
    this.replaceable = replaceable;
    internalName = Descriptor.toJvmName(file.getName());
    this.loader = loader;
    superClassName = file.getSuperclass();
    boolean finalMethod = false;
    Set<MethodData> meths = new HashSet<MethodData>();
    for (Object o : file.getMethods()) {
      String methodClassName = className;
      MethodInfo m = (MethodInfo) o;
      MemberType type = MemberType.NORMAL;
      if ((m.getDescriptor().equals(Constants.ADDED_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_STATIC_METHOD_DESCRIPTOR)
              && m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME))
          || (m.getDescriptor().equals(Constants.ADDED_CONSTRUCTOR_DESCRIPTOR))) {
        type = MemberType.ADDED_SYSTEM;
      } else if (m.getAttribute(Constants.FINAL_METHOD_ATTRIBUTE) != null) {
        finalMethod = true;
      }

      MethodData md =
          new MethodData(
              m.getName(),
              m.getDescriptor(),
              methodClassName,
              type,
              m.getAccessFlags(),
              finalMethod);
      meths.add(md);
    }
    this.methods = Collections.unmodifiableSet(meths);
    Set<FieldData> fieldData = new HashSet<FieldData>();
    for (Object o : file.getFields()) {
      FieldInfo m = (FieldInfo) o;
      MemberType mt = MemberType.NORMAL;
      fieldData.add(new FieldData(m, mt, className, m.getAccessFlags()));
    }
    this.fields = Collections.unmodifiableSet(fieldData);
  }
 public void testInnerClassAttributeRemove() throws Exception {
   CtClass cc = sloader.get("test5.InnerClassRemove");
   ClassFile cf = cc.getClassFile();
   InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag);
   String second = ica.innerClass(1);
   String secondName = ica.innerName(1);
   String third = ica.innerClass(2);
   String thirdName = ica.innerName(2);
   assertEquals(3, ica.remove(3));
   assertEquals(2, ica.remove(0));
   assertEquals(second, ica.innerClass(0));
   assertEquals(secondName, ica.innerName(0));
   assertEquals(third, ica.innerClass(1));
   assertEquals(thirdName, ica.innerName(1));
   assertEquals(1, ica.remove(1));
   assertEquals(second, ica.innerClass(0));
   assertEquals(secondName, ica.innerName(0));
   cc.writeFile();
   Object obj = make(cc.getName());
   assertEquals(1, invoke(obj, "run"));
 }
 public String apply(StaticFieldDescriptor from) {
   // this is quite simple. First we create a proxy
   String proxyName = ProxyDefinitionStore.getProxyName();
   ClassFile proxy = new ClassFile(false, proxyName, "java.lang.Object");
   ClassDataStore.instance().registerProxyName(from.getClazz(), proxyName);
   proxy.setAccessFlags(AccessFlag.PUBLIC);
   FieldInfo newField =
       new FieldInfo(proxy.getConstPool(), from.getName(), from.getDescriptor());
   newField.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
   if (from.getSigniture() != null) {
     SignatureAttribute sig =
         new SignatureAttribute(proxy.getConstPool(), from.getSigniture());
     newField.addAttribute(sig);
   }
   try {
     proxy.addField(newField);
     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
     DataOutputStream dos = new DataOutputStream(bytes);
     try {
       proxy.write(dos);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
     ProxyDefinitionStore.saveProxyDefinition(
         from.getClazz().getClassLoader(), proxyName, bytes.toByteArray());
   } catch (DuplicateMemberException e) {
     // can't happen
   }
   return proxyName;
 }
  /**
   * When creates the delegate initializer code when the delegate is injected into a method.
   *
   * <p>super initializer method is called first, and then _initMH is called
   *
   * @param file
   * @param intializerMethodInfo
   * @param delegateParameterPosition
   * @return
   */
  private Bytecode createDelegateInitializerCode(
      ClassFile file, MethodInformation intializerMethodInfo, int delegateParameterPosition) {
    Bytecode b = new Bytecode(file.getConstPool());
    // we need to push all the pareters on the stack to call the corresponding
    // superclass arguments
    b.addAload(0); // load this
    int localVariables = 1;
    int actualDelegateParamterPosition = 0;
    for (int i = 0; i < intializerMethodInfo.getMethod().getParameterTypes().length; ++i) {
      if (i == delegateParameterPosition) {
        // figure out the actual position of the delegate in the local
        // variables
        actualDelegateParamterPosition = localVariables;
      }
      Class<?> type = intializerMethodInfo.getMethod().getParameterTypes()[i];
      BytecodeUtils.addLoadInstruction(
          b, DescriptorUtils.classToStringRepresentation(type), localVariables);
      if (type == long.class || type == double.class) {
        localVariables = localVariables + 2;
      } else {
        localVariables++;
      }
    }
    b.addInvokespecial(
        file.getSuperclass(), intializerMethodInfo.getName(), intializerMethodInfo.getDescriptor());
    // if this method returns a value it is now sitting on top of the stack
    // we will leave it there are return it later

    // now we need to call _initMH
    b.addAload(0); // load this
    b.addAload(actualDelegateParamterPosition); // load the delegate
    b.addInvokevirtual(file.getName(), "_initMH", "(Ljava/lang/Object;)V");
    // return the object from the top of the stack that we got from calling
    // the superclass method earlier
    BytecodeUtils.addReturnInstruction(b, intializerMethodInfo.getReturnType());
    b.setMaxLocals(localVariables);
    return b;
  }
    protected void writeOutChanges() {
      log.info("writing injection changes back [" + classFileLocation.getAbsolutePath() + "]");
      long timeStamp = classFileLocation.lastModified();
      ClassFile classFile = ctClass.getClassFile();
      classFile.compact();
      try {
        DataOutputStream out =
            new DataOutputStream(new BufferedOutputStream(new FileOutputStream(classFileLocation)));
        try {

          classFile.write(out);
          out.flush();
          if (!classFileLocation.setLastModified(System.currentTimeMillis())) {
            log.info("Unable to manually update class file timestamp");
          }
        } finally {
          out.close();
          classFileLocation.setLastModified(timeStamp);
        }
      } catch (IOException e) {
        throw new InjectionException("Unable to write out modified class file", e);
      }
    }
 @Override
 public void handleChanges(Collection<WatchServiceFileSystemWatcher.FileChangeEvent> changes) {
   try {
     List<AddedClass> addedClasses = new ArrayList<>();
     List<ClassDefinition> changedClasses = new ArrayList<>();
     for (WatchServiceFileSystemWatcher.FileChangeEvent change : changes) {
       if (change.getType() == WatchServiceFileSystemWatcher.FileChangeEvent.Type.ADDED) {
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           ClassFile file = new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
           addedClasses.add(new AddedClass(file.getName(), bytes, classLoader));
         }
       } else if (change.getType()
           == WatchServiceFileSystemWatcher.FileChangeEvent.Type.MODIFIED) {
         String hash = hashes.get(change.getFile().getCanonicalPath());
         if (hash == null) {
           // class is not loaded yet
           continue;
         }
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           if (!hash.equals(MD5.md5(bytes))) {
             ClassFile file =
                 new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
             changedClasses.add(
                 new ClassDefinition(classLoader.loadClass(file.getName()), bytes));
           }
         }
       }
     }
     Agent.redefine(
         changedClasses.toArray(new ClassDefinition[changedClasses.size()]),
         addedClasses.toArray(new AddedClass[addedClasses.size()]));
   } catch (Exception e) {
     e.printStackTrace();
   }
 }
    private JPackage assignPackage(File e) {

      JPackage retVal;

      final File[] classes = e.listFiles(CLASSFILE_FILTER);
      for (int i = 0; i < classes.length; i++) {

        ClassFile classFile =
            FileUtils.doWithFile(
                classes[i],
                new FileInputStreamOperation<ClassFile>() {

                  @Override
                  public ClassFile execute(String path, InputStream fileInputStream) {

                    try {
                      return JavassistClassFileHelper.constructClassFile(path, fileInputStream);
                    } catch (IOException e) {
                      throw new FileAccessException(
                          "Cannot access class file: " + e.getMessage(), e);
                    }
                  }
                });

        try {
          Package pkg = Class.forName(classFile.getName()).getPackage();
          retVal = JPackage.getJPackage(pkg, ClasspathResolver.this);
        } catch (ClassNotFoundException ex) {
          throw new FileAccessException("Cannot access class file: " + ex.getMessage(), ex);
        }

        if (retVal != null) {
          return retVal;
        }
      }
      return null;
    }
  @Override
  public boolean transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      ClassFile file)
      throws IllegalClassFormatException, BadBytecode {

    /**
     * Hack up the proxy factory so it stores the proxy ClassFile. We need this to regenerate
     * proxies.
     */
    if (file.getName().equals("org.jboss.weld.bean.proxy.ProxyFactory")) {
      for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) {
        if (method.getName().equals("createProxyClass")) {
          final MethodInvokationManipulator methodInvokationManipulator =
              new MethodInvokationManipulator();
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              ClassLoader.class.getName(),
              WeldProxyClassLoadingDelegate.class.getName(),
              "loadClass",
              "(Ljava/lang/String;)Ljava/lang/Class;",
              "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
              loader);
          methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic(
              "org.jboss.weld.util.bytecode.ClassFileUtils",
              WeldProxyClassLoadingDelegate.class.getName(),
              "toClass",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
              loader);
          HashSet<MethodInfo> modifiedMethods = new HashSet<MethodInfo>();
          methodInvokationManipulator.transformClass(file, loader, true, modifiedMethods);
          for (MethodInfo m : modifiedMethods) {
            m.rebuildStackMap(ClassPool.getDefault());
          }
          return true;
        } else if (method.getName().equals("<init>")) {

          Integer beanArgument = null;
          int count = 0;
          for (final String paramType :
              DescriptorUtils.descriptorStringToParameterArray(method.getDescriptor())) {
            if (paramType.equals("javax/enterprise/inject/spi/Bean")) {
              beanArgument = count;
              break;
            } else if (paramType.equals("D") || paramType.equals("J")) {
              count += 2;
            } else {
              count++;
            }
          }
          if (beanArgument == null) {
            log.error(
                "Constructor org.jboss.weld.bean.proxy.ProxyFactory.<init>"
                    + method.getDescriptor()
                    + " does not have a bean parameter, proxies produced by this factory will not be reloadable");
            continue;
          }

          // similar to other tracked instances
          // but we need a strong ref
          Bytecode code = new Bytecode(file.getConstPool());
          code.addAload(0);
          code.addAload(beanArgument);
          code.addInvokestatic(
              WeldClassChangeAware.class.getName(),
              "addProxyFactory",
              "(Lorg/jboss/weld/bean/proxy/ProxyFactory;)V");
          CodeIterator it = method.getCodeAttribute().iterator();
          it.skipConstructor();
          it.insert(code.get());
        }
      }
    }
    return false;
  }
  /** Add a method to a class that simply delegates to the parent implementation of the method */
  public static void addDelegatingMethod(ClassFile file, MethodData mData)
      throws BadBytecode, DuplicateMemberException {
    MethodInfo m =
        new MethodInfo(file.getConstPool(), mData.getMethodName(), mData.getDescriptor());
    m.setAccessFlags(mData.getAccessFlags());
    Bytecode code = new Bytecode(file.getConstPool());

    String[] params = DescriptorUtils.descriptorStringToParameterArray(mData.getDescriptor());
    code.add(Opcode.ALOAD_0); // push this
    int count = 1; // zero is the this pointer
    int maxLocals = 1;
    for (String p : params) {
      // int char short boolean byte
      if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) {
        // push integer 0
        code.addIload(count);
        maxLocals++;
      }
      // long
      else if (p.equals("J")) {
        code.addLload(count);
        maxLocals += 2;
        count++;
      }
      // double
      else if (p.equals("D")) {
        code.addDload(count);
        maxLocals += 2;
        count++;
      }
      // float
      else if (p.equals("F")) {
        code.addFload(count);
        maxLocals++;
      }
      // arrays and reference types
      else {
        code.addAload(count);
        maxLocals++;
      }
      count++;
    }
    code.addInvokespecial(file.getSuperclass(), mData.getMethodName(), mData.getDescriptor());
    String p = DescriptorUtils.getReturnTypeInJvmFormat(mData.getDescriptor());
    // int char short boolean byte
    if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) {
      code.add(Opcode.IRETURN);
    }
    // long
    else if (p.equals("J")) {
      code.add(Opcode.LRETURN);
    }
    // double
    else if (p.equals("D")) {
      code.add(Opcode.DRETURN);
    }
    // float
    else if (p.equals("F")) {
      code.add(Opcode.FRETURN);
    }
    // void
    else if (p.equals("V")) {
      code.add(Opcode.RETURN);
    }
    // arrays and reference types
    else {
      code.add(Opcode.ARETURN);
    }
    CodeAttribute ca = code.toCodeAttribute();
    ca.computeMaxStack();
    ca.setMaxLocals(maxLocals);
    m.setCodeAttribute(ca);
    file.addMethod(m);
  }
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    // Be careful with Apache library usage in this class (e.g. ArrayUtils). Usage will likely cause
    // a ClassCircularityError
    // under JRebel. Favor not including outside libraries and unnecessary classes.
    CtClass clazz = null;
    try {
      boolean mySkipOverlaps = skipOverlaps;
      boolean myRenameMethodOverlaps = renameMethodOverlaps;
      String convertedClassName = className.replace('/', '.');
      ClassPool classPool = null;
      String xformKey = convertedClassName;
      String[] xformVals = null;
      Boolean[] xformSkipOverlaps = null;
      Boolean[] xformRenameMethodOverlaps = null;
      if (!xformTemplates.isEmpty()) {
        if (xformTemplates.containsKey(xformKey)) {
          xformVals = xformTemplates.get(xformKey).split(",");
          classPool = ClassPool.getDefault();
          clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
        }
      } else {
        if (annotationTransformedClasses.contains(convertedClassName)) {
          logger.warn(
              convertedClassName
                  + " has already been transformed by a previous instance of DirectCopyTransfomer. "
                  + "Skipping this annotation based transformation. Generally, annotation-based transformation is handled "
                  + "by bean id blAnnotationDirectCopyClassTransformer with template tokens being added to "
                  + "blDirectCopyTransformTokenMap via EarlyStageMergeBeanPostProcessor.");
        }
        boolean isValidPattern = true;
        List<DirectCopyIgnorePattern> matchedPatterns = new ArrayList<DirectCopyIgnorePattern>();
        for (DirectCopyIgnorePattern pattern : ignorePatterns) {
          boolean isPatternMatch = false;
          for (String patternString : pattern.getPatterns()) {
            isPatternMatch = convertedClassName.matches(patternString);
            if (isPatternMatch) {
              break;
            }
          }
          if (isPatternMatch) {
            matchedPatterns.add(pattern);
          }
          isValidPattern = !(isPatternMatch && pattern.getTemplateTokenPatterns() == null);
          if (!isValidPattern) {
            return null;
          }
        }
        if (isValidPattern) {
          classPool = ClassPool.getDefault();
          clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
          List<?> attributes = clazz.getClassFile().getAttributes();
          Iterator<?> itr = attributes.iterator();
          List<String> templates = new ArrayList<String>();
          List<Boolean> skips = new ArrayList<Boolean>();
          List<Boolean> renames = new ArrayList<Boolean>();
          check:
          {
            while (itr.hasNext()) {
              Object object = itr.next();
              if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
                AnnotationsAttribute attr = (AnnotationsAttribute) object;
                Annotation[] items = attr.getAnnotations();
                for (Annotation annotation : items) {
                  String typeName = annotation.getTypeName();
                  if (typeName.equals(DirectCopyTransform.class.getName())) {
                    ArrayMemberValue arrayMember =
                        (ArrayMemberValue) annotation.getMemberValue("value");
                    for (MemberValue arrayMemberValue : arrayMember.getValue()) {
                      AnnotationMemberValue member = (AnnotationMemberValue) arrayMemberValue;
                      Annotation memberAnnot = member.getValue();
                      ArrayMemberValue annot =
                          (ArrayMemberValue) memberAnnot.getMemberValue("templateTokens");
                      for (MemberValue memberValue : annot.getValue()) {
                        String val = ((StringMemberValue) memberValue).getValue();
                        if (val != null && templateTokens.containsKey(val)) {
                          templateCheck:
                          {
                            for (DirectCopyIgnorePattern matchedPattern : matchedPatterns) {
                              for (String ignoreToken : matchedPattern.getTemplateTokenPatterns()) {
                                if (val.matches(ignoreToken)) {
                                  break templateCheck;
                                }
                              }
                            }
                            templates.add(templateTokens.get(val));
                          }
                        }
                      }
                      BooleanMemberValue skipAnnot =
                          (BooleanMemberValue) memberAnnot.getMemberValue("skipOverlaps");
                      if (skipAnnot != null) {
                        skips.add(skipAnnot.getValue());
                      } else {
                        skips.add(mySkipOverlaps);
                      }
                      BooleanMemberValue renameAnnot =
                          (BooleanMemberValue) memberAnnot.getMemberValue("renameMethodOverlaps");
                      if (renameAnnot != null) {
                        renames.add(renameAnnot.getValue());
                      } else {
                        renames.add(myRenameMethodOverlaps);
                      }
                    }
                    xformVals = templates.toArray(new String[templates.size()]);
                    xformSkipOverlaps = skips.toArray(new Boolean[skips.size()]);
                    xformRenameMethodOverlaps = renames.toArray(new Boolean[renames.size()]);
                    break check;
                  }
                }
              }
            }
          }
        }
      }
      if (xformVals != null && xformVals.length > 0) {
        logger.lifecycle(
            LifeCycleEvent.START,
            String.format(
                "Transform - Copying into [%s] from [%s]",
                xformKey, StringUtils.join(xformVals, ",")));
        // Load the destination class and defrost it so it is eligible for modifications
        clazz.defrost();

        int index = 0;
        for (String xformVal : xformVals) {
          // Load the source class
          String trimmed = xformVal.trim();
          classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader()));
          CtClass template = classPool.get(trimmed);

          // Add in extra interfaces
          CtClass[] interfacesToCopy = template.getInterfaces();
          for (CtClass i : interfacesToCopy) {
            checkInterfaces:
            {
              CtClass[] myInterfaces = clazz.getInterfaces();
              for (CtClass myInterface : myInterfaces) {
                if (myInterface.getName().equals(i.getName())) {
                  if (xformSkipOverlaps[index]) {
                    break checkInterfaces;
                  } else {
                    throw new RuntimeException(
                        "Duplicate interface detected " + myInterface.getName());
                  }
                }
              }
              logger.debug(String.format("Adding interface [%s]", i.getName()));
              clazz.addInterface(i);
            }
          }

          // copy over any EntityListeners
          ClassFile classFile = clazz.getClassFile();
          ClassFile templateFile = template.getClassFile();
          ConstPool constantPool = classFile.getConstPool();
          buildClassLevelAnnotations(classFile, templateFile, constantPool);

          // Copy over all declared fields from the template class
          // Note that we do not copy over fields with the @NonCopiedField annotation
          CtField[] fieldsToCopy = template.getDeclaredFields();
          for (CtField field : fieldsToCopy) {
            if (field.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding field [%s]", field.getName()));
            } else {
              try {
                CtField ctField = clazz.getDeclaredField(field.getName());
                String originalSignature = ctField.getSignature();
                String mySignature = field.getSignature();
                if (!originalSignature.equals(mySignature)) {
                  throw new IllegalArgumentException(
                      "Field with name ("
                          + field.getName()
                          + ") and signature "
                          + "("
                          + field.getSignature()
                          + ") is targeted for weaving into ("
                          + clazz.getName()
                          + "). "
                          + "An incompatible field of the same name and signature of ("
                          + ctField.getSignature()
                          + ") "
                          + "already exists. The field in the target class should be updated to a different name, "
                          + "or made to have a matching type.");
                }
                if (xformSkipOverlaps[index]) {
                  logger.debug(String.format("Skipping overlapped field [%s]", field.getName()));
                  continue;
                }
              } catch (NotFoundException e) {
                // do nothing -- field does not exist
              }
              logger.debug(String.format("Adding field [%s]", field.getName()));
              CtField copiedField = new CtField(field, clazz);

              boolean defaultConstructorFound = false;

              String implClass = getImplementationType(field.getType().getName());

              // Look through all of the constructors in the implClass to see
              // if there is one that takes zero parameters
              try {
                CtConstructor[] implConstructors = classPool.get(implClass).getConstructors();
                if (implConstructors != null) {
                  for (CtConstructor cons : implConstructors) {
                    if (cons.getParameterTypes().length == 0) {
                      defaultConstructorFound = true;
                      break;
                    }
                  }
                }
              } catch (NotFoundException e) {
                // Do nothing -- if we don't find this implementation, it's probably because it's
                // an array. In this case, we will not initialize the field.
              }

              if (defaultConstructorFound) {
                clazz.addField(copiedField, "new " + implClass + "()");
              } else {
                clazz.addField(copiedField);
              }
            }
          }

          // Copy over all declared methods from the template class
          CtMethod[] methodsToCopy = template.getDeclaredMethods();
          for (CtMethod method : methodsToCopy) {
            if (method.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding method [%s]", method.getName()));
            } else {
              try {
                CtClass[] paramTypes = method.getParameterTypes();
                CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes);

                if (xformSkipOverlaps[index]) {
                  logger.debug(
                      String.format(
                          "Skipping overlapped method [%s]", methodDescription(originalMethod)));
                  continue;
                }

                if (transformedMethods.contains(methodDescription(originalMethod))) {
                  throw new RuntimeException(
                      "Method already replaced " + methodDescription(originalMethod));
                } else {
                  logger.debug(
                      String.format("Marking as replaced [%s]", methodDescription(originalMethod)));
                  transformedMethods.add(methodDescription(originalMethod));
                }

                logger.debug(String.format("Removing method [%s]", method.getName()));
                if (xformRenameMethodOverlaps[index]) {
                  originalMethod.setName(renameMethodPrefix + method.getName());
                } else {
                  clazz.removeMethod(originalMethod);
                }
              } catch (NotFoundException e) {
                // Do nothing -- we don't need to remove a method because it doesn't exist
              }

              logger.debug(String.format("Adding method [%s]", method.getName()));
              CtMethod copiedMethod = new CtMethod(method, clazz, null);
              clazz.addMethod(copiedMethod);
            }
          }
          index++;
        }

        if (xformTemplates.isEmpty()) {
          annotationTransformedClasses.add(convertedClassName);
        }
        logger.lifecycle(
            LifeCycleEvent.END,
            String.format(
                "Transform - Copying into [%s] from [%s]",
                xformKey, StringUtils.join(xformVals, ",")));
        return clazz.toBytecode();
      }
    } catch (ClassCircularityError error) {
      error.printStackTrace();
      throw error;
    } catch (Exception e) {
      throw new RuntimeException("Unable to transform class", e);
    } finally {
      if (clazz != null) {
        clazz.detach();
      }
    }

    return null;
  }
  /**
   * Calls methodHandler.invoke with a null method parameter in order to get the underlying
   * instance. The invocation is then forwarded to this instance with generated bytecode.
   */
  protected Bytecode createForwardingMethodBody(ClassFile file, MethodInformation methodInfo)
      throws NotFoundException {
    Method method = methodInfo.getMethod();
    // we can only use bytecode based invocation for some methods
    // at the moment we restrict it solely to public methods with public
    // return and parameter types
    boolean bytecodeInvocationAllowed =
        Modifier.isPublic(method.getModifiers())
            && Modifier.isPublic(method.getReturnType().getModifiers());
    for (Class<?> paramType : method.getParameterTypes()) {
      if (!Modifier.isPublic(paramType.getModifiers())) {
        bytecodeInvocationAllowed = false;
        break;
      }
    }
    if (!bytecodeInvocationAllowed) {
      return createInterceptorBody(file, methodInfo);
    }
    Bytecode b = new Bytecode(file.getConstPool());
    int localCount = MethodUtils.calculateMaxLocals(method) + 1;

    // create a new interceptor invocation context whenever we invoke a method on a client proxy
    // we use a try-catch block in order to make sure that endInterceptorContext() is invoked
    // regardless whether
    // the method has succeeded or not
    int start = b.currentPc();
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext",
        "startInterceptorContext",
        "()V");

    b.add(Opcode.ALOAD_0);
    b.addGetfield(
        file.getName(),
        "methodHandler",
        DescriptorUtils.classToStringRepresentation(MethodHandler.class));
    // pass null arguments to methodHandler.invoke
    b.add(Opcode.ALOAD_0);
    b.add(Opcode.ACONST_NULL);
    b.add(Opcode.ACONST_NULL);
    b.add(Opcode.ACONST_NULL);

    // now we have all our arguments on the stack
    // lets invoke the method
    b.addInvokeinterface(
        MethodHandler.class.getName(),
        "invoke",
        "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
        5);

    b.addCheckcast(methodInfo.getDeclaringClass());

    // now we should have the target bean instance on top of the stack
    // we need to dup it so we still have it to compare to the return value
    b.add(Opcode.DUP);

    // lets create the method invocation
    String methodDescriptor = methodInfo.getDescriptor();
    BytecodeUtils.loadParameters(b, methodDescriptor);
    if (method.getDeclaringClass().isInterface()) {
      b.addInvokeinterface(
          methodInfo.getDeclaringClass(),
          methodInfo.getName(),
          methodDescriptor,
          method.getParameterTypes().length + 1);
    } else {
      b.addInvokevirtual(methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor);
    }

    // end the interceptor context, everything was fine
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V");

    // jump over the catch block
    b.addOpcode(Opcode.GOTO);
    JumpMarker gotoEnd = JumpUtils.addJumpInstruction(b);

    // create catch block
    b.addExceptionHandler(start, b.currentPc(), b.currentPc(), 0);
    b.addInvokestatic(
        "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V");
    b.add(Opcode.ATHROW);

    // update the correct address to jump over the catch block
    gotoEnd.mark();

    // if this method returns a primitive we just return
    if (method.getReturnType().isPrimitive()) {
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
    } else {
      // otherwise we have to check that the proxy is not returning 'this;
      // now we need to check if the proxy has return 'this' and if so return
      // an
      // instance of the proxy.
      // currently we have result, beanInstance on the stack.
      b.add(Opcode.DUP_X1);
      // now we have result, beanInstance, result
      // we need to compare result and beanInstance

      // first we need to build up the inner conditional that just returns
      // the
      // result
      b.add(Opcode.IF_ACMPEQ);
      JumpMarker returnInstruction = JumpUtils.addJumpInstruction(b);
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
      returnInstruction.mark();

      // now add the case where the proxy returns 'this';
      b.add(Opcode.ALOAD_0);
      b.addCheckcast(methodInfo.getMethod().getReturnType().getName());
      BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType());
    }
    if (b.getMaxLocals() < localCount) {
      b.setMaxLocals(localCount);
    }
    return b;
  }
 @Override
 protected AnnotationsAttribute getAnnotationsAttribute() {
   return (AnnotationsAttribute) classFile.getAttribute(VISIBLE);
 }
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    if (infos.isEmpty()) {
      return null;
    }
    String convertedClassName = className.replace('/', '.');
    SingleTableInheritanceInfo key = new SingleTableInheritanceInfo();
    key.setClassName(convertedClassName);
    int pos = infos.indexOf(key);
    if (pos >= 0) {
      try {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Converting " + convertedClassName + " to a SingleTable inheritance strategy.");
        }
        SingleTableInheritanceInfo myInfo = infos.get(pos);
        ClassFile classFile =
            new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
        ConstPool constantPool = classFile.getConstPool();
        AnnotationsAttribute annotationsAttribute =
            new AnnotationsAttribute(constantPool, AnnotationsAttribute.visibleTag);
        List<?> attributes = classFile.getAttributes();
        Iterator<?> itr = attributes.iterator();
        while (itr.hasNext()) {
          Object object = itr.next();
          if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
            AnnotationsAttribute attr = (AnnotationsAttribute) object;
            Annotation[] items = attr.getAnnotations();
            for (Annotation annotation : items) {
              String typeName = annotation.getTypeName();
              if (!typeName.equals(Inheritance.class.getName())
                  && ((myInfo.getDiscriminatorName() == null
                          && !typeName.equals(Table.class.getName()))
                      || myInfo.getDiscriminatorName() != null)) {
                annotationsAttribute.addAnnotation(annotation);
              }
            }
            itr.remove();
          }
        }
        Annotation inheritance = new Annotation(Inheritance.class.getName(), constantPool);
        ClassPool pool = ClassPool.getDefault();
        pool.importPackage("javax.persistence");
        pool.importPackage("java.lang");
        EnumMemberValue strategy =
            (EnumMemberValue)
                Annotation.createMemberValue(constantPool, pool.makeClass("InheritanceType"));
        strategy.setType(InheritanceType.class.getName());
        strategy.setValue(InheritanceType.SINGLE_TABLE.name());
        inheritance.addMemberValue("strategy", strategy);
        annotationsAttribute.addAnnotation(inheritance);
        if (myInfo.getDiscriminatorName() != null) {
          Annotation discriminator =
              new Annotation(DiscriminatorColumn.class.getName(), constantPool);
          StringMemberValue name = new StringMemberValue(constantPool);
          name.setValue(myInfo.getDiscriminatorName());
          discriminator.addMemberValue("name", name);
          EnumMemberValue discriminatorType =
              (EnumMemberValue)
                  Annotation.createMemberValue(constantPool, pool.makeClass("DiscriminatorType"));
          discriminatorType.setType(DiscriminatorType.class.getName());
          discriminatorType.setValue(myInfo.getDiscriminatorType().name());
          discriminator.addMemberValue("discriminatorType", discriminatorType);
          IntegerMemberValue length = new IntegerMemberValue(constantPool);
          length.setValue(myInfo.getDiscriminatorLength());
          discriminator.addMemberValue("length", length);

          annotationsAttribute.addAnnotation(discriminator);
        }
        classFile.addAttribute(annotationsAttribute);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream os = new DataOutputStream(bos);
        classFile.write(os);
        os.close();

        return bos.toByteArray();
      } catch (Exception ex) {
        ex.printStackTrace();
        throw new IllegalClassFormatException(
            "Unable to convert "
                + convertedClassName
                + " to a SingleTable inheritance strategy: "
                + ex.getMessage());
      }
    } else {
      return null;
    }
  }
  @Override
  public byte[] transform(
      final ClassLoader loader,
      final String className,
      final Class<?> classBeingRedefined,
      final ProtectionDomain protectionDomain,
      final byte[] classfileBuffer)
      throws IllegalClassFormatException {

    if (integrationClassTriggers.containsKey(className)) {
      integrationClassloader.put(loader, new Object());
      // we need to load the class in another thread
      // otherwise it will not go through the javaagent
      ThreadLoader.loadAsync(
          integrationClassTriggers.get(className).getClassChangeAwareName(), loader, true);
    }

    boolean changed = false;
    if (UnmodifiedFileIndex.isClassUnmodified(className)) {
      // TODO: enable this
      return null;
    }

    final ClassFile file;
    try {
      file = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
      for (final FakereplaceTransformer transformer : transformers) {
        if (transformer.transform(loader, className, classBeingRedefined, protectionDomain, file)) {
          changed = true;
        }
      }

      if (!changed) {
        UnmodifiedFileIndex.markClassUnmodified(className);
        return null;
      } else {
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        file.write(new DataOutputStream(bs));
        // dump the class for debugging purposes
        if (DefaultEnvironment.getEnvironment().getDumpDirectory() != null
            && classBeingRedefined != null) {
          FileOutputStream s =
              new FileOutputStream(
                  DefaultEnvironment.getEnvironment().getDumpDirectory()
                      + '/'
                      + file.getName()
                      + ".class");
          DataOutputStream dos = new DataOutputStream(s);
          file.write(dos);
          s.close();
        }
        return bs.toByteArray();
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new IllegalClassFormatException(e.getMessage());
    } catch (Throwable e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }