private Object addArrayValue(
      Object proxy, String name, Class itemType, Class expectedReturnType, Object arg) {
    if (arrays == null) arrays = new HashMap<String, JAnnotationArrayMember>();
    JAnnotationArrayMember m = arrays.get(name);
    if (m == null) {
      m = use.paramArray(name);
      arrays.put(name, m);
    }

    // sub annotation
    if (Annotation.class.isAssignableFrom(itemType)) {
      Class<? extends Annotation> r = (Class<? extends Annotation>) itemType;
      if (!JAnnotationWriter.class.isAssignableFrom(expectedReturnType))
        throw new IllegalArgumentException("Unexpected return type " + expectedReturnType);
      return new TypedAnnotationWriter(r, expectedReturnType, m.annotate(r)).createProxy();
    }

    // primitive
    if (arg instanceof JType) {
      checkType(Class.class, itemType);
      m.param((JType) arg);
      return proxy;
    }
    checkType(arg.getClass(), itemType);
    if (arg instanceof String) {
      m.param((String) arg);
      return proxy;
    }
    if (arg instanceof Boolean) {
      m.param((Boolean) arg);
      return proxy;
    }
    if (arg instanceof Integer) {
      m.param((Integer) arg);
      return proxy;
    }
    if (arg instanceof Class) {
      m.param((Class) arg);
      return proxy;
    }
    // TODO: enum constant. how should we handle it?

    throw new IllegalArgumentException("Unable to handle this method call ");
  }
  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());
  }