public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    if (method.getDeclaringClass() == JAnnotationWriter.class) {
      try {
        return method.invoke(this, args);
      } catch (InvocationTargetException e) {
        throw e.getTargetException();
      }
    }

    String name = method.getName();
    Object arg = null;
    if (args != null && args.length > 0) arg = args[0];

    // check how it's defined on the annotation
    Method m = annotation.getDeclaredMethod(name);
    Class<?> rt = m.getReturnType();

    // array value
    if (rt.isArray()) {
      return addArrayValue(proxy, name, rt.getComponentType(), method.getReturnType(), arg);
    }

    // sub annotation
    if (Annotation.class.isAssignableFrom(rt)) {
      Class<? extends Annotation> r = (Class<? extends Annotation>) rt;
      return new TypedAnnotationWriter(r, method.getReturnType(), use.annotationParam(name, r))
          .createProxy();
    }

    // scalar value

    if (arg instanceof JType) {
      JType targ = (JType) arg;
      checkType(Class.class, rt);
      if (m.getDefaultValue() != null) {
        // check the default
        if (targ.equals(targ.owner().ref((Class) m.getDefaultValue()))) return proxy; // defaulted
      }
      use.param(name, targ);
      return proxy;
    }

    // other Java built-in types
    checkType(arg.getClass(), rt);
    if (m.getDefaultValue() != null && m.getDefaultValue().equals(arg))
      // defaulted. no need to write out.
      return proxy;

    if (arg instanceof String) {
      use.param(name, (String) arg);
      return proxy;
    }
    if (arg instanceof Boolean) {
      use.param(name, (Boolean) arg);
      return proxy;
    }
    if (arg instanceof Integer) {
      use.param(name, (Integer) arg);
      return proxy;
    }
    if (arg instanceof Class) {
      use.param(name, (Class) arg);
      return proxy;
    }
    if (arg instanceof Enum) {
      use.param(name, (Enum) arg);
      return proxy;
    }

    throw new IllegalArgumentException("Unable to handle this method call " + method.toString());
  }