@SuppressWarnings("deprecation")
  private JRExprFunctionBean createJRFunction(List<Method> methods, Class<?> clazz) {
    JRExprFunctionBean funct = new JRExprFunctionBean(clazz.getCanonicalName());
    // The first instance is the one annotated with @JRFunction
    // that maintains all the necessary infos to prepare the skeleton of the function bean
    Method first = methods.get(0);
    funct.setId(first.getName());
    funct.setReturnType(first.getReturnType());

    String functionMessBundle = getFunctionMessBundle(clazz);
    FunctionMessagesBundle functionMessBundleAnn =
        first.getAnnotation(FunctionMessagesBundle.class);
    if (functionMessBundleAnn == null) {
      functionMessBundleAnn = clazz.getAnnotation(FunctionMessagesBundle.class);
    }
    if (functionMessBundleAnn != null) {
      functionMessBundle = functionMessBundleAnn.value();
    }

    String functionName = null;
    String functionDescription = null;

    Function newFunctionAnnotation = first.getAnnotation(Function.class);
    if (newFunctionAnnotation == null) {
      JRExprFunction functionAnnotation = first.getAnnotation(JRExprFunction.class);
      functionName = functionAnnotation.name();
      functionDescription = functionAnnotation.description();
    } else {
      functionName = newFunctionAnnotation.value();

      String name =
          messageUtil
              .getMessageProvider(functionMessBundle)
              .getMessage(
                  clazz.getName() + "." + funct.getId() + ".name",
                  null,
                  Locale.getDefault()); // FIXMEFUNCT provide locale
      if (name != null) {
        functionName = name;
      }
      String description =
          messageUtil
              .getMessageProvider(functionMessBundle)
              .getMessage(
                  clazz.getName() + "." + funct.getId() + ".description",
                  null,
                  Locale.getDefault()); // FIXMEFUNCT provide locale
      if (description != null) {
        functionDescription = description;
      }
    }

    funct.setName(functionName);
    funct.setDescription(functionDescription);

    JRExprFunctionCategories functionCategoriesAnnotation =
        first.getAnnotation(JRExprFunctionCategories.class);
    if (functionCategoriesAnnotation == null) {
      FunctionCategories newFunctionCategoriesAnnotation =
          first.getAnnotation(FunctionCategories.class);

      if (newFunctionCategoriesAnnotation == null) {
        newFunctionCategoriesAnnotation = clazz.getAnnotation(FunctionCategories.class);
      }

      if (newFunctionCategoriesAnnotation != null) {
        Class<?>[] categories = newFunctionCategoriesAnnotation.value();
        for (Class<?> categoryClass : categories) {
          String categoryId = categoryClass.getName();
          String categoryName = null;
          String categoryDescription = null;

          FunctionCategory functionCategory = categoryClass.getAnnotation(FunctionCategory.class);
          if (functionCategory != null) {
            String id = functionCategory.value();
            if (id != null && id.trim().length() > 0) {
              categoryId = id;
            }
            categoryName = functionCategory.value();
          }

          String categoryMessBundle = getFunctionMessBundle(categoryClass);
          FunctionMessagesBundle categMessBundleAnn =
              categoryClass.getAnnotation(FunctionMessagesBundle.class);
          if (categMessBundleAnn != null) {
            categoryMessBundle = categMessBundleAnn.value();
          }

          String name =
              messageUtil
                  .getMessageProvider(categoryMessBundle)
                  .getMessage(
                      categoryId + ".name",
                      null,
                      Locale.getDefault()); // FIXMEFUNCT provide locale and optimize by getting
          // localized provider everywhere
          if (name != null) {
            categoryName = name;
          }
          String description =
              messageUtil
                  .getMessageProvider(categoryMessBundle)
                  .getMessage(
                      categoryId + ".description",
                      null,
                      Locale.getDefault()); // FIXMEFUNCT provide locale
          if (description != null) {
            categoryDescription = description;
          }

          JRExprFunctionCategoryBean categDescriptor = new JRExprFunctionCategoryBean();
          categDescriptor.setId(categoryId);
          categDescriptor.setName(categoryName);
          categDescriptor.setDescription(categoryDescription);
          funct.getCategories().add(categDescriptor);
        }
      }
    } else {
      for (String category : functionCategoriesAnnotation.value()) {
        JRExprFunctionCategoryBean categDescriptor = new JRExprFunctionCategoryBean();
        categDescriptor.setId(category);
        categDescriptor.setName(
            messageUtil
                .getMessageProvider("MessagesBundle")
                .getMessage("Category." + category + ".display", null, Locale.getDefault()));
        categDescriptor.setDescription(
            messageUtil
                .getMessageProvider("MessagesBundle")
                .getMessage("Category." + category + ".description", null, Locale.getDefault()));
        funct.getCategories().add(categDescriptor);
      }
    }

    FunctionParameters newParametersAnnotation = first.getAnnotation(FunctionParameters.class);
    if (newParametersAnnotation == null) {
      JRExprFunctionParameters parametersAnnotation =
          first.getAnnotation(JRExprFunctionParameters.class);
      if (parametersAnnotation != null) {
        for (JRExprFunctionParameter param : parametersAnnotation.value()) {
          // Get basic info from the annotation
          JRExprFunctionParameterBean paramDescriptor = new JRExprFunctionParameterBean();
          paramDescriptor.setName(param.name());
          paramDescriptor.setDescription(param.description());
          funct.getParameters().add(paramDescriptor);
        }
      }
    } else {
      for (FunctionParameter param : newParametersAnnotation.value()) {
        // Get basic info from the annotation
        JRExprFunctionParameterBean paramDescriptor = new JRExprFunctionParameterBean();
        String parameterId = param.value();
        String parameterName = null;
        String parameterDescription = null;
        if (parameterId != null && parameterId.trim().length() > 0) {
          String name =
              messageUtil
                  .getMessageProvider(functionMessBundle)
                  .getMessage(
                      clazz.getName() + "." + funct.getId() + "." + parameterId + ".name",
                      null,
                      Locale.getDefault()); // FIXMEFUNCT provide locale
          if (name != null) {
            parameterName = name;
          }
          String description =
              messageUtil
                  .getMessageProvider(functionMessBundle)
                  .getMessage(
                      clazz.getName() + "." + funct.getId() + "." + parameterId + ".description",
                      null,
                      Locale.getDefault()); // FIXMEFUNCT provide locale
          if (description != null) {
            parameterDescription = description;
          }
        }
        paramDescriptor.setName(parameterName);
        paramDescriptor.setDescription(parameterDescription);
        funct.getParameters().add(paramDescriptor);
      }
    }

    // Now computes the mandatory and cardinality of the parameters
    int paramIndex = 0;
    int paramsNum = funct.getParameters().size();
    for (int i = 0; i < methods.size() && paramIndex < paramsNum; i++) {
      Method currMethod = methods.get(i);
      Class<?>[] parameterTypes = currMethod.getParameterTypes();
      boolean isOptional =
          (i == 0) ? false : true; // all parameters of the first method are for sure mandatory	
      for (int j = paramIndex; j < parameterTypes.length; j++, paramIndex++) {
        boolean isMulti = parameterTypes[j].isArray();
        JRExprFunctionParameterBean paramFunctBean = funct.getParameters().get(paramIndex);
        if (paramFunctBean != null) {
          paramFunctBean.setOptional(isOptional);
          paramFunctBean.setMulti(isMulti);
          paramFunctBean.setParameterType(parameterTypes[j]);
        } else {
          // ERROR params number mismatch in descriptions/names/types
          // TODO issue Exception or log error (?!)
        }
      }
    }

    return funct;
  }
 private JRExprAnnotationsUtils(JasperReportsContext jasperReportsContext) {
   this.jasperReportsContext = jasperReportsContext;
   this.messageUtil = MessageUtil.getInstance(jasperReportsContext);
 }