如何在注解处理器(编译时)内部检查包是否存在?

13

我正在为Android构建一个注解处理器(为了以后的解释,我们将其称为TestProcessor)。

计划是让处理器在两种模式下运行:

  • 模式1:生成代码 A

  • 模式2:生成代码 A B


模式2只有在处理器生成代码的环境中存在额外(可选)包时才应选择。

是否有办法在注解处理器的process方法中找出可选包是否存在?


编辑1:

关于“(可选)包”的详细说明。通过附加软件包,我指的是可能但不必须在使用处理器的项目中存在的Java软件包。可选包可以代表外部库的内容。外部库可以包含在项目中,但不必如此。

让我举个小例子:

  • 假设我们有一个(单模块)项目Sample
  • Sample正在使用我的TestProcessor
  • 以Square的Picasso作为外部库的例子
如果Sample的依赖中包含Square's Picasso,则TestProcessor处于Mode 2模式并生成代码AB。换句话说:如果可以在Sample中使用com.square.picasso的类,则处理器应该处于Mode 2模式。
如果Sample的依赖中没有Square's Picasso,则TestProcessor处于Mode 1模式并只生成代码A
编辑2:
我想到了两个解决这个问题的方法:
1.为两个processor modes使用两种类型的注释(例如:@DoStuffModeOne@DoStuffModeTwo)。
2.使用第二种类型的注释(例如在Application上使用),触发使用Mode 2(例如:@TriggerModeTwo)。
对于我的情况,第二种解决方法更可取,但仍然比处理器自己决定选择哪种Mode要糟糕得多。

“附加(可选)包”究竟是什么?你是指Java包吗?如果是,那么Java包存在意味着什么?你是说该Java包的目录存在吗?你是说该目录中存在一个源文件吗?我没有编写过注释处理器,所以无法直接帮助您,但编辑您的问题以澄清您所指的内容可能会帮助其他人帮助您。 - CommonsWare
当然,让我澄清一下... - Bartek Lipinski
是的,我是。目前我想检查项目中是否存在“butterknife”,但这个问题更加普遍。 - Bartek Lipinski
“不同的输出”不会是一个问题。它不会让用户感到困惑或误导,因为如果不使用可选包,附加代码(代码B)就没有任何意义,更可能会导致构建失败(因为缺少引用)。至于不同类型的注释,那是我正在考虑的解决方法之一。让我编辑我的问题,包括这个和我正在考虑的第二个解决方法。 - Bartek Lipinski
不存在包的存在这种事情。只有该包中的类、接口或资源的存在或不存在。 - user207421
显示剩余7条评论
3个回答

2

您可以检查应用程序类路径中的一个ButterKnife类。然后根据检查结果执行您的逻辑。

Class butterKnifeClass = null;
try {
    butterKnifeClass = Class.forName("butterknife.ButterKnife");
} catch (ClassNotFoundException e) {
    // no butterknife present
}
if (butterKnifeClass != null) {
    // blah... stuff with butterknife
}

1
唯一的问题是,它只会在编译器中没有“可选包”(在这个例子中是butterknife)的情况下才能工作。我知道我最初没有提到这个要求,所以你目前是最好的假装者,可以获得奖励 :) 让我再等几天看看是否还有更多的答案出现。 - Bartek Lipinski

2
您可以使用以下代码来查找注解处理环境中是否存在某个类。
TypeElement typeElement = processingEnvironment.getElementUtils().
    getTypeElement("com.squareup.picasso.Picasso");

我使用 Elements.getPackageElement() 方法没有成功,因为即使在不存在的包下(至少在Eclipse下),它也返回成功,并且对于像这样创建的包运行 PackageElement.getEnclosedElements() 方法为空,即使对于非空的包也是如此。我建议您选择感兴趣的包中的特定类,就像我的先前示例一样,因为它可以正常工作并对于不存在的类型返回 null

目前我没有找到基于注解处理API的方法来判断以这种方式找到的类型元素是基于项目源路径上的源代码文件还是编译后在类路径上的二进制类文件。可能有一种方法涉及到 processingEnvironment.getFiler().getResource(StandardLocation.SOURCE_PATH, ...),但不幸的是它不具有可移植性,因为在Eclipse中不支持 StandardLocation.SOURCE_PATH

希望这可以帮助您。


谢谢您的回答。不幸的是,它并没有解决问题。我需要检查它是否存在于正在进行注释处理的源代码中。 - Bartek Lipinski
那是否意味着您需要支持基于JDK的注释处理构建和基于Eclipse的构建?JDK应该可以使用processingEnvironment.getFiler().getResource(StandardLocation.SOURCE_PATH, ...)来工作。Eclipse也可以工作,但有点不太好看。 - Nándor Előd Fekete
Eclipse区分基于源文件或二进制类文件的TypeElement。这不是注释处理API公开的,但是在检测到实际上是Eclipse编译器运行您的处理器后,您可以通过反射轻松获取它。或者,您可以将相关的Eclipse插件API包含到您的代码中,并进行显式转换,然后从那里决定是否基于Java源文件或二进制类。我知道这不是正确的做法,但如果Eclipse决定拒绝从SOURCE_PATH获取资源,我们能做什么呢? - Nándor Előd Fekete

0
除了JBirdVegas提出的解决方案之外,此函数可用于在运行时检查外部库中是否存在软件包。
public static boolean packageExists(String packagePath) {
  return Thread.currentThread().getContextClassLoader().getResource(packagePath) != null;
}

使用方法:

packageExists("apackage/innerpackage")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接