private void genInterfacePropertyDecl(StringBuilder sb, IPropertyInfo pi, IJavaType javaType) {
   if (pi.isStatic()) {
     genStaticProperty(pi, sb, javaType);
     return;
   }
   if (!pi.isReadable()) {
     return;
   }
   if (!(pi instanceof JavaBaseFeatureInfo)) {
     // It is possible that a methodinfo on a java type originates outside of java.
     // E.g., enhancement methods. Gosu does not support extending these.
     return;
   }
   IType type = getGenericType(pi);
   if (pi.getDescription() != null) {
     sb.append("/** ").append(pi.getDescription()).append(" */");
   }
   sb.append(" property get ")
       .append(pi.getName())
       .append("() : ")
       .append(type.getName())
       .append("\n");
   if (pi.isWritable(pi.getOwnersType())) {
     sb.append(" property set ")
         .append(pi.getName())
         .append("( _proxy_arg_value : ")
         .append(type.getName())
         .append(" )\n");
   }
 }
  private void genProperty(IPropertyInfo pi, StringBuilder sb, IJavaType type) {
    if (pi.isPrivate() || pi.isInternal()) {
      return;
    }

    if (pi.isStatic()) {
      genStaticProperty(pi, sb, type);
    } else {
      genMemberProperty(pi, sb, type);
    }
  }
  private void genStaticProperty(IPropertyInfo pi, StringBuilder sb, IJavaType type) {
    if (!pi.isStatic()) {
      return;
    }

    if (!(pi instanceof JavaBaseFeatureInfo)) {
      // It is possible that a methodinfo on a java type originates outside of java.
      // E.g., enhancement methods. Gosu does not support extending these.
      return;
    }

    if (Keyword.isReserved(pi.getName())) {
      // Sorry these won't compile
      // ## todo: handle them reflectively?
      return;
    }

    if (pi.getDescription() != null) {
      sb.append("/** ").append(pi.getDescription()).append(" */");
    }
    if (pi instanceof JavaFieldPropertyInfo) {
      StringBuilder sbModifiers = appendFieldVisibilityModifier(pi);
      sb.append("  ")
          .append(sbModifiers)
          .append("static var ")
          .append(pi.getName())
          .append(" : ")
          .append(getGenericType(pi).getName())
          .append("\n");
    } else {
      StringBuilder sbModifiers = appendVisibilityModifier(pi);
      sb.append("  ")
          .append(sbModifiers)
          .append("static property get ")
          .append(pi.getName())
          .append("() : ")
          .append(getGenericType(pi).getName())
          .append("\n")
          .append("  {\n")
          .append("    return ")
          .append(type.getName())
          .append('.')
          .append(pi.getName())
          .append(";\n")
          .append("  }\n");

      if (pi.isWritable(pi.getOwnersType())) {
        sb.append("  static property set ")
            .append(pi.getName())
            .append("( _proxy_arg_value : ")
            .append(getGenericType(pi).getName())
            .append(" )\n")
            .append("  {\n")
            .append("  ")
            .append(type.getName())
            .append('.')
            .append(pi.getName())
            .append(" = _proxy_arg_value;\n")
            .append("  }\n");
      }
    }
  }
  private void genMemberProperty(IPropertyInfo pi, StringBuilder sb, IJavaType type) {
    if (pi.isStatic()) {
      return;
    }

    if (!(pi instanceof JavaBaseFeatureInfo)) {
      // It is possible that a propertyinfo on a java type originates outside of java.
      // E.g., enhancement properties. Gosu does not support extending these.
      return;
    }

    if (Keyword.isReserved(pi.getName()) && !Keyword.isReservedValue(pi.getName())) {
      // Sorry these won't compile
      // ## todo: handle them reflectively?
      return;
    }

    if (pi instanceof JavaFieldPropertyInfo) {
      StringBuilder sbModifiers = appendFieldVisibilityModifier(pi);
      sb.append("  ")
          .append(sbModifiers)
          .append(" var ")
          .append(pi.getName())
          .append(" : ")
          .append(getGenericType(pi).getName())
          .append("\n");
    } else {
      IMethodInfo mi = getPropertyGetMethod(pi, type);
      boolean bFinal = false;
      if (mi != null) {
        int iMethodModifiers = ((IJavaMethodInfo) mi).getModifiers();
        bFinal = java.lang.reflect.Modifier.isFinal(iMethodModifiers);
      }

      if (mi != null && !bFinal) {
        if (mi.getDescription() != null) {
          sb.append("/** ").append(mi.getDescription()).append(" */");
        }
        StringBuilder sbModifiers = buildModifiers(mi);
        sb.append("  ")
            .append(sbModifiers)
            .append("property get ")
            .append(pi.getName())
            .append("() : ")
            .append(getGenericType(pi).getName())
            .append("\n");
        if (!mi.isAbstract()) {
          generateStub(sb, mi.getReturnType());
        }
      } else {
        String strJavaClassName = type.getName();
        StringBuilder sbModifiers;
        boolean bAbstact = false;
        if (bFinal) {
          bAbstact = mi.isAbstract();
          sbModifiers = buildModifiers(mi);
        } else {
          sbModifiers = appendVisibilityModifier(pi);
        }
        sb.append("  ")
            .append(sbModifiers)
            .append("property get ")
            .append(pi.getName())
            .append("() : ")
            .append(getGenericType(pi).getName())
            .append("\n");
        if (!bAbstact) {
          generateStub(sb, getGenericType(pi));
        }
      }

      mi = getPropertySetMethod(pi, type);
      bFinal = false;
      if (mi != null) {
        int iMethodModifiers = ((IJavaMethodInfo) mi).getModifiers();
        bFinal = java.lang.reflect.Modifier.isFinal(iMethodModifiers);
      }

      if (mi != null && !bFinal) {
        StringBuilder sbModifiers = buildModifiers(mi);
        if (pi.isWritable(pi.getOwnersType())) {
          sb.append("  ")
              .append(sbModifiers)
              .append("property set ")
              .append(pi.getName())
              .append("( _proxy_arg_value : ")
              .append(getGenericType(pi).getName())
              .append(" )\n");
          if (!mi.isAbstract()) {
            generateStub(sb, JavaTypes.pVOID());
          }
        }
      } else {
        if (pi.isWritable(type.getEnclosingType() != null ? null : pi.getOwnersType())) {
          String strJavaClassName = type.getName();
          StringBuilder sbModifiers;
          boolean bAbstact = false;
          if (bFinal) {
            bAbstact = mi.isAbstract();
            sbModifiers = buildModifiers(mi);
          } else {
            sbModifiers = appendVisibilityModifier(pi);
          }
          sb.append("  ")
              .append(sbModifiers)
              .append("property set ")
              .append(pi.getName())
              .append("( _proxy_arg_value : ")
              .append(getGenericType(pi).getName())
              .append(" )\n");
          if (!bAbstact) {
            generateStub(sb, JavaTypes.pVOID());
          }
        }
      }
    }
  }
  private static Options deriveOptionsFromTypeInfo(
      ITypeInfo typeInfo, /*IN-OUT*/ List<IPropertyInfo> propsToSet, boolean useStaticProps) {
    List<? extends IPropertyInfo> propertyInfos = typeInfo.getProperties();
    Options options = new Options();
    for (IPropertyInfo propertyInfo : propertyInfos) {
      if (propertyInfo instanceof IGosuVarPropertyInfo) {
        IGosuVarPropertyInfo gsVarPropInfo = (IGosuVarPropertyInfo) propertyInfo;
        if (gsVarPropInfo.hasDeclaredProperty() || gsVarPropInfo.isFinal()) {
          continue;
        }
      }
      if (useStaticProps == propertyInfo.isStatic() && propertyInfo.isWritable(null)) {
        String shortName = getShortName(propertyInfo);
        boolean needsArg = needsArg(propertyInfo);

        GosuOption opt =
            new GosuOption(
                shortName, getLongName(propertyInfo), needsArg, deriveDescription(propertyInfo));
        opt.setHidden(propertyInfo.hasAnnotation(TypeSystem.get(Hidden.class)));

        opt.setRequired(propertyInfo.hasAnnotation(TypeSystem.get(Required.class)));
        if (propertyInfo.getFeatureType().isArray()) {
          opt.setType(propertyInfo.getFeatureType().getComponentType().getName());

          if (propertyInfo.hasAnnotation(TypeSystem.get(ArgNames.class))) {
            ArgNames argNames =
                (ArgNames)
                    propertyInfo
                        .getAnnotationsOfType(TypeSystem.get(ArgNames.class))
                        .get(0)
                        .getInstance();
            if (argNames.names() != null && argNames.names().length > 0) {
              String compoundNames = "";
              for (int i = 0; i < argNames.names().length; i++) {
                if (i != 0) {
                  compoundNames += " ";
                }
                compoundNames += argNames.names()[i];
              }
              opt.setArgName(compoundNames);
              if (propertyInfo.hasAnnotation(TypeSystem.get(ArgOptional.class))) {
                opt.setOptionalArg(true);
              }
              opt.setArgs(argNames.names().length);
            }
          } else {
            opt.setArgs(Option.UNLIMITED_VALUES);
          }

          if (propertyInfo.hasAnnotation(TypeSystem.get(Separator.class))) {
            Separator argNames =
                (Separator)
                    propertyInfo
                        .getAnnotationsOfType(TypeSystem.get(Separator.class))
                        .get(0)
                        .getInstance();
            opt.setValueSeparator(argNames.value().charAt(0));
          }

        } else {
          opt.setType(propertyInfo.getFeatureType().getName());
          if (propertyInfo.hasAnnotation(TypeSystem.get(ArgNames.class))) {
            ArgNames argNames =
                (ArgNames)
                    propertyInfo
                        .getAnnotationsOfType(TypeSystem.get(ArgNames.class))
                        .get(0)
                        .getInstance();
            if (argNames.names() != null && argNames.names().length > 0) {
              opt.setArgName(argNames.names()[0]);
            }
          }
        }

        propsToSet.add(propertyInfo);
        options.addOption(opt);
      }
    }
    return options;
  }