/** Extract the Type information from a TYPE node. */
  private static String _typeName(IterableTree ast) {
    if (ast == null) {
      return null;
    }
    StringBuilder typeName = new StringBuilder();

    if (ast.getType() == JavaParser.TYPE) {

      IterableTree qualifiedType = ast.firstChildOfType(JavaParser.QUALIFIED_TYPE_IDENT);

      if (qualifiedType != null) {
        StringBuilder qualifiedTypeName = new StringBuilder();
        for (int i = 0; i < qualifiedType.getChildCount(); i++) {
          qualifiedTypeName.append("." + qualifiedType.getChild(i).getText());

          IterableTree generics =
              qualifiedType.getChild(i).firstChildOfType(JavaParser.GENERIC_TYPE_ARG_LIST);
          if (generics != null) {
            qualifiedTypeName.append('<');
            qualifiedTypeName.append(_genericsTypeList(generics));
            qualifiedTypeName.append('>');
          }
        }
        /* to remove the leading dot */
        typeName.append(qualifiedTypeName.substring(1));
      } else {
        typeName.append(ast.getChild(0).getText());
      }

      Tree arrayDecl = ast.firstChildOfType(JavaParser.ARRAY_DECLARATOR_LIST);
      if (arrayDecl != null) {
        for (int i = 0; i < arrayDecl.getChildCount(); i++) {
          typeName.append("[]");
        }
      }

    } else if (ast.getType() == JavaParser.QUESTION) {
      typeName.append("?");

      if (ast.getChildCount() > 0) {
        /* ^(QUESTION ^(EXTENDS|SUPER type)) */
        IterableTree extendsAst = ast.getChild(0);
        typeName.append(" ");
        typeName.append(extendsAst.getText());
        typeName.append(" ");
        typeName.append(_typeName(extendsAst.getChild(0)));
      }
    }

    return typeName.toString();
  }