/**
   * The resource parameters for a root resource include the constructor params.
   *
   * @param delegate The declaration.
   * @return The resource params.
   */
  @Override
  protected List<ResourceParameter> getResourceParameters(TypeDeclaration delegate) {
    List<ResourceParameter> resourceParams = super.getResourceParameters(delegate);

    if (getDelegate() == delegate && delegate instanceof ClassDeclaration) {
      // root resources also include constructor params.

      Collection<ConstructorDeclaration> constructors =
          ((ClassDeclaration) delegate).getConstructors();
      ConstructorDeclaration chosen = null;
      CONSTRUCTOR_LOOP:
      for (ConstructorDeclaration constructor : constructors) {
        // the one with the most params is the chosen one.
        if (chosen == null || constructor.getParameters().size() > chosen.getParameters().size()) {
          // Has more constructor parameters.  See if they're all Jersey-provided.
          for (ParameterDeclaration param : constructor.getParameters()) {
            if (!ResourceParameter.isResourceParameter(param)) {
              continue CONSTRUCTOR_LOOP;
            }
          }
          chosen = constructor;
        }
      }

      if (chosen != null) {
        chosen = DeclarationDecorator.decorate(chosen);
        for (ParameterDeclaration param : chosen.getParameters()) {
          resourceParams.add(new ResourceParameter(param));
        }
      }
    }

    return resourceParams;
  }
  @Nonnull
  public ConstructorCallInfo findConstructorCallInfoForField(
      @Nonnull String simpleName, @Nonnull TypeMirror type) throws IllegalArgumentException {
    ConstructorDeclaration constructorDeclaration = TypeUtils.findBestConstructor(classDeclaration);

    int index = 0;

    for (ParameterDeclaration parameterDeclaration : constructorDeclaration.getParameters()) {
      if (parameterDeclaration.getSimpleName().equals(simpleName)) {
        // Found a fitting type
        if (TypeUtils.mightBeConstructorCallFor(parameterDeclaration.getType(), type)) {
          return new ConstructorCallInfo(constructorDeclaration, index, parameterDeclaration);
        } else {
          throw new IllegalArgumentException(
              "Type mismatch for <"
                  + simpleName
                  + ">. Was <"
                  + parameterDeclaration.getType()
                  + "> but expected <"
                  + type
                  + ">");
        }
      }
      index++;
    }

    throw new IllegalArgumentException("No parameter found that fits <" + simpleName + ">");
  }
  private void processDefaultMethods(Thing data, ClassDeclaration classDeclaration) {
    // find any methods that have default parameters
    boolean error = false;
    for (ConstructorDeclaration constructorDeclaration : classDeclaration.getConstructors()) {
      Collection<ParameterDeclaration> parameters = constructorDeclaration.getParameters();
      for (ParameterDeclaration parameterDeclaration : parameters) {
        Default annotation = parameterDeclaration.getAnnotation(Default.class);
        if (annotation != null) {
          error(parameterDeclaration, "@Default is not legal in constructor parameters");
          error = true;
        }
      }
    }
    if (error) {
      return;
    }

    boolean atLeastOneDefault = false;
    methods:
    for (MethodDeclaration methodDeclaration : classDeclaration.getMethods()) {
      Collection<ParameterDeclaration> parameters = methodDeclaration.getParameters();
      boolean seenDefault = false;
      String[] names = new String[parameters.size()];
      String[] types = new String[parameters.size()];
      String[] defaults = new String[parameters.size()];
      int n = 0;
      for (ParameterDeclaration parameterDeclaration : parameters) {
        Default annotation = parameterDeclaration.getAnnotation(Default.class);
        names[n] = parameterDeclaration.getSimpleName();
        types[n] = parameterDeclaration.getType().toString();
        if (annotation != null) {
          seenDefault = true;
          if ("java.lang.String".equals(types[n])) {
            defaults[n] = '"' + annotation.value() + '"';
          } else {
            defaults[n] = annotation.value();
          }
        } else if (seenDefault) {
          error(
              parameterDeclaration,
              "All parameters after a parameter annotated with @Default must be annotated with @Default");
          continue methods;
        }
        n++;
      }

      if (seenDefault) {
        atLeastOneDefault = true;
        if (methodDeclaration.getModifiers().contains(Modifier.PRIVATE)) {
          error(methodDeclaration, "Private methods cannot use @Default parameters");
        }
        if (methodDeclaration.getModifiers().contains(Modifier.STATIC)) {
          error(methodDeclaration, "Static methods cannot use @Default parameters");
        }
        String modifiers3 = "";
        if (methodDeclaration.getModifiers().contains(Modifier.PUBLIC)) {
          modifiers3 = "public ";
        } else if (methodDeclaration.getModifiers().contains(Modifier.PROTECTED)) {
          modifiers3 = "protected ";
        }
        String throwsClause = getThrowsClause(methodDeclaration);
        String returnType = methodDeclaration.getReturnType().toString();
        String methodName = methodDeclaration.getSimpleName();
        String argDecl = "";
        String callArgs = "";
        for (int i = 0; i < n; i++) {
          if (defaults[i] != null) {
            String callArgsWithDefaults = callArgs;
            for (int j = i; j < n; j++) {
              if (j > 0) {
                callArgsWithDefaults += ", ";
              }
              callArgsWithDefaults += defaults[j];
            }
            Thing method = new Thing("method");
            method.put("name", methodName);
            method.put("returnType", returnType);
            method.put("throwsClause", throwsClause);
            method.put("argDecls", argDecl);
            method.put("modifiers", modifiers3);
            method.put("args", callArgsWithDefaults);
            data.add("defaultMethods", method);
          }
          if (i > 0) {
            argDecl += ", ";
            callArgs += ", ";
          }
          argDecl += types[i] + ' ' + names[i];
          callArgs += names[i];
        }
        Thing method = new Thing("method");
        method.put("name", methodName);
        method.put("returnType", returnType);
        method.put("throwsClause", throwsClause);
        method.put("modifiers", modifiers3);
        method.put("abstract", true);
        method.put("argDecls", argDecl);
        data.add("defaultMethods", method);
      }
    }
    data.put("atLeastOneDefault", atLeastOneDefault);
    if (!atLeastOneDefault) {
      data.setEmpty("defaultMethods");
    }
  }