private static void addSubTypes(
     StringBuffer ret, GenericsType[] types, String start, String end) {
   if (types == null) return;
   ret.append(start);
   for (int i = 0; i < types.length; i++) {
     if (types[i].getType().isArray()) {
       ret.append("[");
       addSubTypes(
           ret,
           new GenericsType[] {new GenericsType(types[i].getType().getComponentType())},
           "",
           "");
     } else {
       if (types[i].isPlaceholder()) {
         ret.append('T');
         String name = types[i].getName();
         ret.append(name);
         ret.append(';');
       } else if (types[i].isWildcard()) {
         if (types[i].getUpperBounds() != null) {
           ret.append('+');
           writeGenericsBounds(ret, types[i], false);
         } else if (types[i].getLowerBound() != null) {
           ret.append('-');
           writeGenericsBounds(ret, types[i], false);
         } else {
           ret.append('*');
         }
       } else {
         writeGenericsBounds(ret, types[i], false);
       }
     }
   }
   ret.append(end);
 }
  public static String getGenericsBounds(ClassNode type) {
    GenericsType[] genericsTypes = type.getGenericsTypes();
    if (genericsTypes == null) return null;
    StringBuffer ret = new StringBuffer(100);
    if (type.isGenericsPlaceHolder()) {
      addSubTypes(ret, type.getGenericsTypes(), "", "");
    } else {
      GenericsType gt = new GenericsType(type);
      writeGenericsBounds(ret, gt, false);
    }

    return ret.toString();
  }
 private static void writeGenericsBoundType(
     StringBuffer ret, ClassNode printType, boolean writeInterfaceMarker) {
   if (writeInterfaceMarker && printType.isInterface()) ret.append(":");
   if (printType.equals(ClassHelper.OBJECT_TYPE) && printType.getGenericsTypes() != null) {
     ret.append("T");
     ret.append(printType.getGenericsTypes()[0].getName());
     ret.append(";");
   } else {
     ret.append(getTypeDescription(printType, false));
     addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
     if (!ClassHelper.isPrimitiveType(printType)) ret.append(";");
   }
 }
  public static String getGenericsMethodSignature(MethodNode node) {
    GenericsType[] generics = node.getGenericsTypes();
    Parameter[] param = node.getParameters();
    ClassNode returnType = node.getReturnType();

    if (generics == null && !hasGenerics(param) && !hasGenerics(returnType)) return null;

    StringBuffer ret = new StringBuffer(100);
    getGenericsTypeSpec(ret, generics);

    GenericsType[] paramTypes = new GenericsType[param.length];
    for (int i = 0; i < param.length; i++) {
      ClassNode pType = param[i].getType();
      if (pType.getGenericsTypes() == null || !pType.isGenericsPlaceHolder()) {
        paramTypes[i] = new GenericsType(pType);
      } else {
        paramTypes[i] = pType.getGenericsTypes()[0];
      }
    }
    addSubTypes(ret, paramTypes, "(", ")");
    addSubTypes(ret, new GenericsType[] {new GenericsType(returnType)}, "", "");
    return ret.toString();
  }