public void addGetter(MetaBeanProperty property) throws Exception {
      MetaMethod getter = property.getGetter();

      // GENERATE private boolean <prop>Set;

      String flagName = String.format("%sSet", property.getName());
      visitor.visitField(
          Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null);

      addConventionGetter(getter.getName(), flagName, property);

      String getterName = getter.getName();
      Class<?> returnType = getter.getReturnType();

      // If it's a boolean property, there can be get or is type variants.
      // If this class has both, decorate both.
      if (returnType.equals(Boolean.TYPE)) {
        boolean getterIsIsMethod = getterName.startsWith("is");
        String propertyNameComponent = getterName.substring(getterIsIsMethod ? 2 : 3);
        String alternativeGetterName =
            String.format("%s%s", getterIsIsMethod ? "get" : "is", propertyNameComponent);

        try {
          type.getMethod(alternativeGetterName);
          addConventionGetter(alternativeGetterName, flagName, property);
        } catch (NoSuchMethodException e) {
          // ignore, no method to override
        }
      }
    }
    public Map<String, ?> getProperties() {
      if (!includeProperties) {
        return Collections.emptyMap();
      }

      Map<String, Object> properties = new HashMap<String, Object>();
      List<MetaProperty> classProperties = getMetaClass().getProperties();
      for (MetaProperty metaProperty : classProperties) {
        if (metaProperty.getName().equals("properties")) {
          properties.put("properties", properties);
          continue;
        }
        if (metaProperty instanceof MetaBeanProperty) {
          MetaBeanProperty beanProperty = (MetaBeanProperty) metaProperty;
          if (beanProperty.getGetter() == null) {
            continue;
          }
        }
        properties.put(metaProperty.getName(), metaProperty.getProperty(bean));
      }
      return properties;
    }
    private void addConventionGetter(String getterName, String flagName, MetaBeanProperty property)
        throws Exception {
      // GENERATE public <type> <getter>() { return
      // (<type>)getConventionMapping().getConventionValue(super.<getter>(), '<prop>', <prop>Set); }
      MetaMethod getter = property.getGetter();

      Type returnType = Type.getType(getter.getReturnType());
      String methodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
      MethodVisitor methodVisitor =
          visitor.visitMethod(
              Opcodes.ACC_PUBLIC, getterName, methodDescriptor, null, new String[0]);
      methodVisitor.visitCode();

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          conventionAwareType.getInternalName(),
          "getConventionMapping",
          Type.getMethodDescriptor(conventionMappingType, new Type[0]));

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKESPECIAL, superclassType.getInternalName(), getterName, methodDescriptor);

      Type boxedType = null;
      if (getter.getReturnType().isPrimitive()) {
        // Box value
        boxedType =
            Type.getType(JavaReflectionUtil.getWrapperTypeForPrimitiveType(getter.getReturnType()));
        String valueOfMethodDescriptor =
            Type.getMethodDescriptor(boxedType, new Type[] {returnType});
        methodVisitor.visitMethodInsn(
            Opcodes.INVOKESTATIC, boxedType.getInternalName(), "valueOf", valueOfMethodDescriptor);
      }

      methodVisitor.visitLdcInsn(property.getName());

      methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
      methodVisitor.visitFieldInsn(
          Opcodes.GETFIELD,
          generatedType.getInternalName(),
          flagName,
          Type.BOOLEAN_TYPE.getDescriptor());

      String getConventionValueDesc =
          Type.getMethodDescriptor(
              ConventionMapping.class.getMethod(
                  "getConventionValue", Object.class, String.class, Boolean.TYPE));
      methodVisitor.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          conventionMappingType.getInternalName(),
          "getConventionValue",
          getConventionValueDesc);

      if (getter.getReturnType().isPrimitive()) {
        // Unbox value
        methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, boxedType.getInternalName());
        String valueMethodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
        methodVisitor.visitMethodInsn(
            Opcodes.INVOKEVIRTUAL,
            boxedType.getInternalName(),
            getter.getReturnType().getName() + "Value",
            valueMethodDescriptor);
      } else {
        // Cast to return type
        methodVisitor.visitTypeInsn(
            Opcodes.CHECKCAST,
            getter.getReturnType().isArray()
                ? "[" + returnType.getElementType().getDescriptor()
                : returnType.getInternalName());
      }

      methodVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
      methodVisitor.visitMaxs(0, 0);
      methodVisitor.visitEnd();
    }
Example #4
0
  public <T> Class<? extends T> generate(Class<T> type) {
    Map<Class, Class> cache = GENERATED_CLASSES.get(getClass());
    if (cache == null) {
      cache = new HashMap<Class, Class>();
      GENERATED_CLASSES.put(getClass(), cache);
    }
    Class generatedClass = cache.get(type);
    if (generatedClass != null) {
      return generatedClass;
    }

    if (Modifier.isPrivate(type.getModifiers())) {
      throw new GradleException(
          String.format(
              "Cannot create a proxy class for private class '%s'.", type.getSimpleName()));
    }
    if (Modifier.isAbstract(type.getModifiers())) {
      throw new GradleException(
          String.format(
              "Cannot create a proxy class for abstract class '%s'.", type.getSimpleName()));
    }

    Class<? extends T> subclass;
    try {
      ClassBuilder<T> builder = start(type);

      boolean isConventionAware = type.getAnnotation(NoConventionMapping.class) == null;
      boolean isDynamicAware = type.getAnnotation(NoDynamicObject.class) == null;

      builder.startClass(isConventionAware, isDynamicAware);

      if (isDynamicAware && !DynamicObjectAware.class.isAssignableFrom(type)) {
        builder.mixInDynamicAware();
      }
      if (isDynamicAware && !GroovyObject.class.isAssignableFrom(type)) {
        builder.mixInGroovyObject();
      }
      if (isDynamicAware) {
        builder.addDynamicMethods();
      }
      if (isConventionAware && !IConventionAware.class.isAssignableFrom(type)) {
        builder.mixInConventionAware();
      }

      Class noMappingClass = Object.class;
      for (Class<?> c = type; c != null && noMappingClass == Object.class; c = c.getSuperclass()) {
        if (c.getAnnotation(NoConventionMapping.class) != null) {
          noMappingClass = c;
        }
      }

      Collection<String> skipProperties =
          Arrays.asList("metaClass", "conventionMapping", "convention", "asDynamicObject");

      MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(type);
      for (MetaProperty property : metaClass.getProperties()) {
        if (skipProperties.contains(property.getName())) {
          continue;
        }
        if (property instanceof MetaBeanProperty) {
          MetaBeanProperty metaBeanProperty = (MetaBeanProperty) property;
          MetaMethod getter = metaBeanProperty.getGetter();
          if (getter == null) {
            continue;
          }
          if (Modifier.isFinal(getter.getModifiers())
              || Modifier.isPrivate(getter.getModifiers())) {
            continue;
          }
          if (getter.getReturnType().isPrimitive()) {
            continue;
          }
          Class declaringClass = getter.getDeclaringClass().getTheClass();
          if (declaringClass.isAssignableFrom(noMappingClass)) {
            continue;
          }
          builder.addGetter(metaBeanProperty);

          MetaMethod setter = metaBeanProperty.getSetter();
          if (setter == null) {
            continue;
          }
          if (Modifier.isFinal(setter.getModifiers())
              || Modifier.isPrivate(setter.getModifiers())) {
            continue;
          }

          builder.addSetter(metaBeanProperty);
        }
      }

      for (Constructor<?> constructor : type.getConstructors()) {
        if (Modifier.isPublic(constructor.getModifiers())) {
          builder.addConstructor(constructor);
        }
      }

      subclass = builder.generate();
    } catch (Throwable e) {
      throw new GradleException(
          String.format("Could not generate a proxy class for class %s.", type.getName()), e);
    }

    cache.put(type, subclass);
    return subclass;
  }