之前提过Multiline注解,可以在低版本的Java语言中通过注释与注解优雅地实现多行文本的定义。将Multiline-String拓展为Multiline-Byte Array后,意犹未尽,于是开了个空工程(纯Java无groovy、maven管理)测试annotation。但是……怎么都不对,注解不生效,都没调用!明明,资源文件中meta-inf信息填好,处理器挂钩,但是写好的注解毛都没处理。一开始以为是idea的问题,换了Eclipse,也还是不行……
索性放弃另起炉灶的想法。干脆再次拓展Multiline模块。这回的需求是,简化程序的标志位操作。我一个项目中差不多用光了4个长整型标志位容器,这之中不单单是一些布尔值,好包括两位到三位的短整数。结果,
管理设置的那个文件变成了这样……
两千多行了……
我要让一个设置就占一两行!
鼓捣一番后后,终于成功:
原先,直接把标志位mask写了出来,现在,我只需配置好方法名称、标志位容器,然后再注解的变量中配置标志位位置、默认值偏移、debug数值等等即可!
以get方法为例,标志位操作的完整的“公式”如下:
- 短整数
(((FirstFlag>>flagPos) & mask)[+shift])[%max][+elevation];
public int eval(int flagPos, int flagSize, int shift, int elevation, int max) {
int mask = (1<<flagSize)-1;
if(max==0)max=mask;
return (int) ((((FirstFlag>>flagPos) & mask)+shift)%max+elevation);
}
shift用于偏移默认值。shift之后的变量皆为可选,所以注解处理器根据变量不同处理生成不同的方法实体。最简短的核心是: (FirstFlag>>flagPos) & mask。max代表最大使用数值,比如一个111b三位短整数标志,容纳的最大数是7,但是有可能只用到了100b,也就是0、1、2、3、4五个数值。
- 布尔标志
public boolean getInPageSearchVisible() {
return (FirstFlag & 0x100000000000l) == 0x100000000000l;
}
注解处理(修改方法体的实现):
JCTree.JCMethodDecl metDcl = (JCTree.JCMethodDecl) elementUtils.getTree(field);
Multiline annotation = field.getAnnotation(Multiline.class);
List<JCTree.JCStatement> statements = metDcl.body.stats;
JCTree.JCPrimitiveTypeTree RETType = (JCTree.JCPrimitiveTypeTree) retType;
- 短整数
JCTree.JCExpressionStatement stat = (JCTree.JCExpressionStatement) _1st;
JCTree.JCAssign assgn = (JCTree.JCAssign) stat.expr;
JCTree.JCExpression flag = (JCTree.JCIdent) assgn.getVariable(); //JCTree.JCIdent
int flagPos = annotation.flagPos();
int mask = (1<<annotation.flagSize())-1;
long maskVal = ((long)mask)<<annotation.flagPos();
int max = annotation.max();
int shift = annotation.shift();
if(max==0) {
max = mask;
}
int elevation = annotation.elevation();
List<JCVariableDecl> parms = metDcl.getParameters();
if(RETType.typetag==TypeTag.INT) {
//CMN.Log("mask", mask);
JCTree.JCBinary core = maker.Binary(JCTree.Tag.SR, flag, maker.Literal(flagPos));
JCTree.JCBinary basic = maker.Binary(JCTree.Tag.BITAND, maker.Parens(core), maker.Literal(mask));
JCTree.JCExpression finalExpr = basic;
if(shift!=0||max<mask) {
if(shift!=0) {
finalExpr = maker.Binary(JCTree.Tag.PLUS, maker.Parens(basic), maker.Literal(shift));
}
finalExpr = maker.Binary(JCTree.Tag.MOD, maker.Parens(finalExpr), maker.Literal(max));
}
if(elevation>0) {
finalExpr = maker.Binary(JCTree.Tag.PLUS, finalExpr, maker.Literal(annotation.elevation()));
}
finalExpr = maker.TypeCast(maker.TypeIdent(TypeTag.INT), finalExpr);
CMN.Log(121,finalExpr);
metDcl.body = maker.Block(0, List.from(new JCTree.JCStatement[]{maker.Return(finalExpr)}));
}
- 布尔变量
else if(RETType.typetag==TypeTag.BOOLEAN) {
int debugVal = annotation.debug();
int size = parms.size();
if(size==1) {
JCVariableDecl val=parms.get(0);
JCTree.JCPrimitiveTypeTree type = (JCTree.JCPrimitiveTypeTree) val.getType();
if(type.typetag==TypeTag.LONG||type.typetag==TypeTag.INT) {
flag = maker.Ident(val);
}
}
JCTree.JCBinary core = maker.Binary(JCTree.Tag.BITAND, flag, maker.Literal(maskVal));
JCTree.JCExpression finalExpr = maker.Binary(shift==0?JCTree.Tag.NE:JCTree.Tag.EQ, maker.Parens(core), maker.Literal(0));
if(debugVal>=0) {
finalExpr = maker.Literal(debugVal==1);
}
metDcl.body = maker.Block(0, List.from(new JCTree.JCStatement[]{maker.Return(finalExpr)}));
}
get的注解实现便如此……
本想试试生成set方法的,但还是一样地弄一遍比较简单。
其中,改变方法体通过 metDcl.body = …… 赋值实现。所有语句均通过 TreeMaker 组装,列表如下:
maker.Literal(val) :“字面义”,用原生数据创建JcExpression
maker.Ident(dcl) :意义不明,Identity吗?用变量声明创建JcExpression
maker.Parens(exp) :套括号
maker.Binary(运算符符Tag,左运算数JcExpression,右运算数JcExpression) :二元操作
maker.Unary(运算符符Tag,运算数JcExpression) : 一元操作
maker.Assign(变量JcExpression,数值JcExpression) :创建赋值表达(JcExpression)
maker.Exec(maker.Assign()) 创建赋值语句 :(JCTree.JCExpressionStatement)
maker.If(条件JcExpression, 真分支JCStatement, 假分支JCStatement) :创建If语句(JCStatement)
……
然后,修改方法体是这样的:
JCTree.JCStatement[] stats = new JCTree.JCStatement[2];
stats[0]=语句……
metDcl.body = maker.Block(0, List.from(stats));
maker.Binary是二元操作,本来还以这是二进制的意思呢,直到看到 maker.Unary ……
一元、二元运算符均通过 JCTree.Tag 标明,列表如下:
JCTree.Tag.BITOR 按位或
JCTree.Tag.BITAND 按位与
JCTree.Tag.SL shift left 左移
JCTree.Tag.SR shift left 右移
JCTree.Tag.EQ 相等
JCTree.Tag.NE 不等
JCTree.Tag.LT 小于
JCTree.Tag.GT 大于
JCTree.Tag.PLUS 加
JCTree.Tag.MOD 取模
JCTree.Tag.NOT 非
……
注解的设计:
@Multiline(flagPos=9, flagSize=2, shift=1, elevation=2, max=0) public int getCycleTyleViaAnnot(){ FirstFlag=FirstFlag; throw new RuntimeException(); }
展开看
@Multiline(flagPos=9, flagSize=2, shift=1, elevation=2, max=0)
public int getCycleTyleViaAnnot()
{
- FirstFlag=FirstFlag; 自赋值,用于注解处理中获取标志位容器的JcExpression。
- throw new RuntimeException(); 直接抛出
}
注解本身的定义:
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Multiline {
boolean trim() default true;
int flagPos() default 0;
int flagSize() default 1;
int shift() default 0;
int elevation() default 0;
int max() default 0;
int debug() default -1;
}
此时Multiline已经不能称作Multiline了,当改名为:
MetaLine · 元线
另外,这是什么意思,idea欺负我不懂英文么?