插入式直接处理器

插入式注解处理器

Sub:CheckStyle,FindBugs,Klocwork实现原理

一.API基础知识

AbstractProcessor

ElementKind

ProcessingEnvironment

SupportedAnnotationTypes

SupportedSourceVersion

二.eg代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
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.TypeElement;

//可以用"*"标识支持所有Annotations
@SupportedAnnotationTypes("*")
//只支持JDK1.6的java代码
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class NameCheckProcessor extends AbstractProcessor {

private NameChecker mNameChecker;

/**
* 初始换名称检查插件
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mNameChecker = new NameChecker(processingEnvironment);
}

/**
* 对输入的语法树的各个节点进行名称检查
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
for (Element element : roundEnvironment.getRootElements()) {
mNameChecker.checkNames(element);
}
}
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import java.util.EnumSet;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementScanner6;
import javax.tools.Diagnostic;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;

/**
* 程序名称规范的编译器插件
* 如果名称不符合规范,将会报出一个编译器的警告信息
* Created by zhouyibo on 2018/7/30.
*/

public class NameChecker {
private final Messager mMessager;
private NameCheckScanner mNameCheckScanner = new NameCheckScanner();

public NameChecker(ProcessingEnvironment processingEnvironment) {
mMessager = processingEnvironment.getMessager();
}

/**
* 对Java程序命名进行检查
* @param element
*/
public void checkNames(Element element) {
mNameCheckScanner.scan(element);
}

/**
* 名称检查实现类 继承ElementScanner6
* 将会以Visitor模式访问抽象语法树中的元素
*/
private class NameCheckScanner extends ElementScanner6<Void, Void> {
/**
* 检查Java类
* @param typeElement
* @param aVoid
* @return
*/
@Override
public Void visitType(TypeElement typeElement, Void aVoid) {
scan(typeElement.getTypeParameters(), aVoid);
checkCamelCase(typeElement, true);
super.visitType(typeElement, aVoid);
return null;
}

/**
* 检查方法名
* @param executableElement
* @param aVoid
* @return
*/
@Override
public Void visitExecutable(ExecutableElement executableElement, Void aVoid) {
if (executableElement.getKind() == ElementKind.METHOD) {
Name name = executableElement.getSimpleName();
if (name.contentEquals(executableElement.getEnclosingElement().getSimpleName())) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "一个普通方法" + name + "不应当与类名重复,避免与构造函数产生混淆", executableElement);
checkCamelCase(executableElement, false);
}
}
super.visitExecutable(executableElement, aVoid);
return null;
}

/**
* 检查变量命名
* @param variableElement
* @param aVoid
* @return
*/
@Override
public Void visitVariable(VariableElement variableElement, Void aVoid) {
if (variableElement.getKind() == ElementKind.ENUM_CONSTANT || variableElement.getConstantValue() != null || heuristicallyConstant(variableElement)) {
checkAllCaps(variableElement);
} else {
checkCamelCase(variableElement, false);
}
return null;
}

/**
* 大写命名检查
* @param e
*/
private void checkAllCaps(Element e) {
String name = e.getSimpleName().toString();
boolean convertional = true;
int firstCodePoint = name.codePointAt(0);
if (!Character.isUpperCase(firstCodePoint)) {
convertional = false;
} else {
boolean previousUnderscore = false;
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (cp == (int) '_') {
if (previousUnderscore) {
convertional = false;
break;
}
previousUnderscore = true;
} else {
previousUnderscore = false;
if (!Character.isUpperCase(cp) && !Character.isDigit(cp)) {
convertional = false;
break;
}
}
}
}
if (!convertional) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "常量" + name + "应当全部以大写字母或者下划线命名,并且以字母开头", e);
}
}

/**
* 检查是都是常量
* @param variableElement
* @return
*/
private boolean heuristicallyConstant(VariableElement variableElement) {
if (variableElement.getEnclosingElement().getKind() == ElementKind.INTERFACE) {
return true;
} else if (variableElement.getKind() == ElementKind.FIELD && variableElement.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL))) {
return true;
} else {
return false;
}
}

/**
* 检查是否符合驼式命名
* @param e
* @param b
*/
private void checkCamelCase(Element e, boolean b) {
String name = e.getSimpleName().toString();
boolean previousUpper = false;
boolean conventional = true;
int firstCodePoint = name.codePointAt(0);
if (Character.isUpperCase(firstCodePoint)) {
previousUpper = true;
if (!b) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "名称" + name + "应当以小写字母开头", e);
return;
}
} else if (Character.isLowerCase(firstCodePoint)) {
if (b) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "名称" + name + "应当以大写字母开头", e);
return;
}
} else {
conventional = false;
}
if (conventional) {
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (Character.isUpperCase(cp)) {
if (previousUpper) {
conventional = false;
break;
}
previousUpper = true;
} else {
previousUpper = false;
}
}
}
if (!conventional) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "名称" + name + "应当符合驼式命名法", e);
}
}
}
}