又一 Java 注解(annotation)用例

之前提过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欺负我不懂英文么?
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值