/**
   * Parses a method type definition
   *
   * @param docMethod
   * @return
   */
  protected static Method ParseMethod(MethodDoc docMethod) {
    assert (docMethod != null);

    Method xmlMethod = new Method();

    xmlMethod.name = docMethod.name();
    xmlMethod.hash = computeHash(docMethod.qualifiedName(), docMethod.signature());
    xmlMethod.qualifiedName = docMethod.qualifiedName();
    xmlMethod.comment = docMethod.commentText();
    xmlMethod.signature = docMethod.signature();
    xmlMethod.isNative = docMethod.isNative();
    xmlMethod.isVarArgs = docMethod.isVarArgs();
    xmlMethod.isSynchronized = docMethod.isSynchronized();
    xmlMethod.isFinal = docMethod.isFinal();
    xmlMethod.isAbstract = docMethod.isAbstract();
    xmlMethod.isStatic = docMethod.isStatic();

    xmlMethod.scope = DetermineScope(docMethod);

    // Parse parameters of the method
    Parameter[] parameters = docMethod.parameters();

    if (parameters != null && parameters.length > 0) {
      ParamTag[] paramComments = docMethod.paramTags();

      ArrayList<Param> paramList = new ArrayList<Param>();

      for (Parameter parameter : parameters) {
        ParamTag paramComment = null;

        // look to see if this parameter has comments
        // if so, paramComment will be set
        for (ParamTag testParam : paramComments) {
          String testParamName = testParam.parameterName();
          if (testParamName != null) {
            if (testParamName.compareTo(parameter.name()) == 0) {
              paramComment = testParam;
              break;
            }
          }
        }

        paramList.add(ParseParameter(parameter, paramComment));
      }

      xmlMethod.parameters = paramList.toArray(new Param[] {});
    } else {
      log.debug("No parameters for method: " + docMethod.name());
    }

    // Parse result data

    Result returnInfo = new Result();

    Tag[] returnTags = docMethod.tags("@return");
    if (returnTags != null && returnTags.length > 0) {
      // there should be only one return tag.  but heck,
      // if they specify two, so what...
      StringBuilder builder = new StringBuilder();
      for (Tag returnTag : returnTags) {
        String returnTagText = returnTag.text();
        if (returnTagText != null) {
          builder.append(returnTagText);
          builder.append("\n");
        }
      }

      returnInfo.comment = builder.substring(0, builder.length() - 1);
    }

    returnInfo.type = ParseType(docMethod.returnType());
    xmlMethod.result = returnInfo;

    // Parse exceptions of the method

    Type[] types = docMethod.thrownExceptionTypes();
    ThrowsTag[] exceptionComments = docMethod.throwsTags();

    if (types != null && types.length > 0) {
      ArrayList<ExceptionInstance> exceptionList = new ArrayList<ExceptionInstance>();

      for (Type exceptionType : types) {
        ExceptionInstance exception = new ExceptionInstance();

        exception.type = ParseType(exceptionType);

        for (ThrowsTag exceptionComment : exceptionComments) {
          if (exceptionType == exceptionComment.exceptionType()) {
            exception.comment = exceptionComment.exceptionComment();

            ClassDoc exceptionDetails = exceptionComment.exception();

            // not yet parsing Exceptions defined within the supplied code set
            exception.type = ParseType(exceptionComment.exceptionType());
            break;
          }
        }

        exceptionList.add(exception);
      }

      xmlMethod.exceptions = exceptionList.toArray(new ExceptionInstance[] {});
    }

    // parse annotations from the method
    xmlMethod.annotationInstances =
        ParseAnnotationInstances(docMethod.annotations(), docMethod.qualifiedName());

    return xmlMethod;
  }
  private static ApiDoc scanMethodDoc(ClassDoc classDoc, MethodDoc methodDoc) {
    String routePrefix = "";

    // RoutePrefix for class
    for (AnnotationDesc annDesc : classDoc.annotations()) {
      if (RoutePrefix.class.getName().equals(annDesc.annotationType().qualifiedTypeName())) {
        routePrefix = annotationToString(annDesc, "value", "");
        break;
      }
    }

    AnnotationDesc[] annDescs = methodDoc.annotations();
    if (ArrayUtils.isEmpty(annDescs)) return null;

    for (AnnotationDesc annDesc : annDescs) {
      if (IgnoreDocument.class.getName().equals(annDesc.annotationType().qualifiedTypeName()))
        return null;
    }

    // RoutePrefix for method
    for (AnnotationDesc annDesc : annDescs) {
      if (RoutePrefix.class.getName().equals(annDesc.annotationType().qualifiedTypeName())) {
        routePrefix = annotationToString(annDesc, "value", "");
        break;
      }
    }

    Class httpExamplePackageClass = null;
    String[] routes = null;
    String[] httpMethods = {"GET", "POST"};
    boolean deprecated = false;
    for (AnnotationDesc annDesc : annDescs) {
      if (Route.class.getName().equals(annDesc.annotationType().qualifiedTypeName())) {
        for (AnnotationDesc.ElementValuePair annValue : annDesc.elementValues()) {
          String name = annValue.element().name();
          if ("url".equals(name)) {
            routes = annotationToStringArray(annValue.value().value());
          } else if ("method".equals(name)) {
            httpMethods = annotationToStringArray(annValue.value().value());
          }
        }
      }

      if (Deprecated.class.getName().equals(annDesc.annotationType().qualifiedTypeName()))
        deprecated = true;

      if (HttpExamplePackage.class.getName().equals(annDesc.annotationType().qualifiedTypeName())) {
        for (AnnotationDesc.ElementValuePair annValue : annDesc.elementValues()) {
          String name = annValue.element().name();
          if ("value".equals(name)) {
            ClassDoc httpExamplePackageClassAnn = (ClassDoc) annValue.value().value();
            httpExamplePackageClass =
                ClassHelper.forName(httpExamplePackageClassAnn.qualifiedTypeName());
          }
        }
      }
    }
    if (routes == null) return null;

    if (httpExamplePackageClass == null) {
      annDescs = classDoc.annotations();
      for (AnnotationDesc annDesc : annDescs) {
        if (HttpExamplePackage.class
            .getName()
            .equals(annDesc.annotationType().qualifiedTypeName())) {
          for (AnnotationDesc.ElementValuePair annValue : annDesc.elementValues()) {
            String name = annValue.element().name();
            if ("value".equals(name)) {
              ClassDoc httpExamplePackageClassAnn = (ClassDoc) annValue.value().value();
              httpExamplePackageClass =
                  ClassHelper.forName(httpExamplePackageClassAnn.qualifiedTypeName());
            }
          }
        }
      }
    }

    ApiDoc apiDoc = new ApiDoc();
    apiDoc.group = getTag(methodDoc, GROUP_TAG, "");
    apiDoc.routes = addRoutePrefix(routes, routePrefix);
    apiDoc.httpMethods = httpMethods;
    apiDoc.description = expandText(methodDoc.commentText());
    apiDoc.login = parseBoolean(getTag(methodDoc, LOGIN_TAG, "y"));
    apiDoc.deprecated = deprecated;
    apiDoc.httpReturn = getTag(methodDoc, HTTP_RETURN, "");

    apiDoc.remark = expandText(getTag(methodDoc, REMARK_TAG, ""));
    apiDoc.className = classDoc.qualifiedTypeName();
    apiDoc.httpExamplePackageClass = httpExamplePackageClass;
    String[] httpExamples = expandTexts(getTags(methodDoc, HTTP_EXAMPLE_TAG));
    for (int i = 0; i < httpExamples.length; i++) {
      String httpExample = httpExamples[i];
      if (httpExample.startsWith("@")) {
        if (apiDoc.httpExamplePackageClass != null) {
          httpExamples[i] =
              VfsHelper.loadTextInClasspath(
                  apiDoc.httpExamplePackageClass, StringUtils.removeStart(httpExample, "@"));
        } else {
          httpExamples[i] = "";
        }
      }
    }

    apiDoc.httpExamples = httpExamples;

    ArrayList<ParamDoc> l = new ArrayList<ParamDoc>();
    for (String httpParamTag : getTags(methodDoc, HTTP_PARAM_TAG)) {
      ParamDoc httpParamDoc = parseParamDoc(httpParamTag);
      if (httpParamDoc != null) l.add(httpParamDoc);
    }
    apiDoc.httpParams = l.toArray(new ParamDoc[l.size()]);
    return apiDoc;
  }