编译方法参数的

时间:2017-03-07 09:10:03

标签: java eclipse validation

我在这里发现了一些类似的问题,但是不完整的答案没有帮助,并且产生了更多的混淆而不是澄清任何事情,所以这是我尝试提出一个更有条理的问题,并希望获得有助于更多用户的答案。

我的简化示例:我有一个带有两个不同构造函数的Java类

public class ObjectOfInterest {
  public ObjectOfInterest(String string, Integer int) { ... }
  public ObjectOfInterest(String string1, String string2) { ... }
  ...
}

我需要对这些构造函数的调用进行一些编译时验证。参数string2必须是一些文字,我想根据内容将调用标记为警告(即发出警告,当它不是文字或文字格式不正确时)。

不幸的是,使用Eclipse进行Java验证的文档不容易理解,有时已经过时,在我看来似乎不完整,似乎没有一个工作实例可以在教程中使用。 / p>

我的目标: 第一步:我想要一个验证器来标记这两个参数版本的调用并带有警告 - 只是为了开始某个地方并了解基础知识。

到目前为止我发现了: 我见过的几个例子是public class MyValidator implements IValidator, ISourceValidator,其中IValidator需要实现方法public void validate(IValidationContext arg0, IReporter arg1) throws ValidationException并且似​​乎来自验证框架的旧版本(有时我发现只有空方法评论无用),ISourceValidator需要实现方法public void validate(IRegion arg0, IValidationContext arg1, IReporter arg2) - 这似乎是更新的版本。

然后你必须为某些plugin.xml添加一些扩展点(我不完全清楚这个plugin.xml的位置)。

我在黑暗中刺伤:我们完全不清楚如何使用IRegionIValidationContextIReporter - 也许我完全不知道错误的方式,但我得到了什么?如何在验证中找到该构造函数的调用?

在第一步更清楚之后,我会扩展这个问题。 Outlook,我想要为构造函数的两个String版本添加快速修复的可能性,并以这种方式操作代码 - 但这至少要提前两步,详情将在稍后发布。

2 个答案:

答案 0 :(得分:3)

首先,我必须说你正在努力超越正常的Java编程。除了使用普通类型可以实现的功能之外,在编译时没有正常的方法进行验证。

您想要做的还超出使用注释处理器可以做的。注释处理器是一种半正常的,因为它们是标准化的并且是Java框架的一部分。它们是在编译期间运行的类,它们将类和方法的签名作为输入,并可用于验证和代码生成。

如果你还在做什么呢有异常的方法,但是:

Eclipse插件解决方案

您似乎正在尝试的解决方案是编写一个Eclipse插件,该插件使用Eclipse Java工具进行验证。这应该有用,我不知道它会有多容易,验证只适用于使用Eclipse的用户。

Checker Solution

根据我(有限)知识看起来最好的工具是:

静态分析的 Checker Framework

它完全用于您想要做的事情。它似乎有充分的文档记录,易于设置。它用于执行空值分析和正则表达式语法的编译时验证。有tutorial听起来与你的东西很相似。有关creating a new checker的手册中有一章。

使用此解决方案可能需要花费大量时间和精力,但我认为它看起来非常有趣!

答案 1 :(得分:1)

  

注意:

     

这可能不是确切的解决方案。但我正在努力讨论   解决问题的可能方法。我已经提到了   可能有助于工作的替代方法。请加入   的讨论

我想尝试使用JDK-5中引入的 Java Annotation Processors ,并在JDK-6规范下JSR-269标准化。

需要注释应使用自定义注释根据规则验证的文件(java @interfaces)。如果无法对每个文件进行注释,则必须注释包含要验证的类的包(也可以迭代内部包)。以下示例将演示如何使用带注释的类和注释处理器验证类。我已将示例项目上传到github存储库。请在github上查看项目存储库https://github.com/hjchanna/compile_validation_java

步骤01:创建注释界面(@interface

package com.mac.compile_validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author hjchanna
 */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface CompileValidation {

}

步骤02:创建一个Processor类,将编译验证规则引入编译器

处理器类应extend来自javax.annotation.processing.AbstractProcessor,并且应使用@SupportedAnnotationTypes@SupportedSourceVersion注释进行注释。请根据具体要求或验证规则修改CompileValidationProcessor类。

package com.mac.compile_validation;

import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;

/**
 *
 * @author hjchanna
 */
@SupportedAnnotationTypes("com.mac.compile_validation.CompileValidation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CompileValidationProcessor extends AbstractProcessor {

    /**
     * Processes a set of annotation types on type elements originating from the
     * prior round and returns whether or not these annotation types are claimed
     * by this processor. If {@code
     * true} is returned, the annotation types are claimed and subsequent
     * processors will not be asked to process them; if {@code false} is
     * returned, the annotation types are unclaimed and subsequent processors
     * may be asked to process them. A processor may always return the same
     * boolean value or may vary the result based on chosen criteria.
     *
     * The input set will be empty if the processor supports {@code
     * "*"} and the root elements have no annotations. A {@code
     * Processor} must gracefully handle an empty set of annotations.
     *
     * @param annotations the annotation types requested to be processed
     * @param roundEnv environment for information about the current and prior
     * round
     * @return whether or not the set of annotation types are claimed by this
     * processor
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //Iterate through compiling files which annotated with @CompileValidation
        for (Element elem : roundEnv.getElementsAnnotatedWith(CompileValidation.class)) {
            //find type element for element
            TypeElement typeElement = findEnclosingTypeElement(elem);

            //required parameter types
            TypeElement stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String");
            TypeElement integerType = processingEnv.getElementUtils().getTypeElement("java.lang.Integer");

            //find construtors according to your scenario
            ExecutableElement conA = findConstructor(typeElement, stringType.asType(), integerType.asType());
            ExecutableElement conB = findConstructor(typeElement, stringType.asType(), stringType.asType());

            //check availability of constructors, if not available it should show a warning message in compile time
            if (conA == null || conB == null) {
                String message = "Type " + typeElement + " has malformed constructors.";
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message);
            }

        }
        return true; // no further processing of this annotation type
    }

    /**
     * Returns a constructor which have two parameters and parameter types equal
     * to paramA and paramB. Return null if required constructor is not
     * available.
     *
     * @param typeElement like the class which may constructors encapsulated
     * @param paramA first parameter of required constructor
     * @param paramB second parameter of required constructor
     * @return constructor which have required parameters
     */
    private static ExecutableElement findConstructor(TypeElement typeElement, TypeMirror paramA, TypeMirror paramB) {
        List<ExecutableElement> executableElements = ElementFilter.constructorsIn(typeElement.getEnclosedElements());

        for (ExecutableElement executableElement : executableElements) {
            List<VariableElement> variableElements = (List<VariableElement>) executableElement.getParameters();

            //match constructor params and length
            if (variableElements.size() == 2
                    && variableElements.get(0).asType().equals(paramA)
                    && variableElements.get(1).asType().equals(paramB)) {
                return executableElement;
            }
        }

        return null;
    }

    /**
     * Returns the TypeElement of element e.
     *
     * @param e Element which contain TypeElement
     * @return Type element
     */
    public static TypeElement findEnclosingTypeElement(Element e) {
        while (e != null && !(e instanceof TypeElement)) {
            e = e.getEnclosingElement();
        }

        return TypeElement.class.cast(e);
    }
}

步骤03:创建处理服务链接文件

然后需要将名为javax.annotation.processing.Processor的类添加到项目的资源路径中(/src/main/resources/META-INF/services)。该文件仅包含Processor的类名。根据前面的例子,配置文件内容如下。

com.mac.compile_validation.CompileValidationProcessor

以前的方法适用于maven项目。如果需要,可以手动将配置文件注入输出META-INF/services文件的.jar文件夹。

步骤04:禁用当前项目的验证

禁用当前项目的注释处理。如果启用它,它将无法构建当前项目,因为编译器尝试找到要验证的Processor类。但它仍然没有编译。因此,它本身就无法构建项目。将以下代码添加到pom.xml(内部<build> -> <plugin>)。

<compilerArgument>-proc:none</compilerArgument>
  

现在差不多完了。唯一需要做的就是将构建输出.jar文件依赖项添加到原始项目中。

是时候测试项目了。使用先前创建的自定义注释(CompileValidation)注释所需的类。如果无法验证带注释的类,它将显示警告。我的输出如下。

build output

  

替代解决方案

  • 可以使用 PMD ,这是一个java源代码扫描程序。它提供了使用xml配置定义规则的方法。
  • 尝试在启动时使用java反射验证类。 (这不是你提出的问题。但是在开始工作之前验证内容是一个很好的做法,例如spring,hibernate和其他知名框架。)
  • 尝试 Java Instrumentation API ,但只有在违反规则的情况下才会导致应用程序崩溃。这不是一个好习惯。