irenkaのHackを書いているとHackの適用を特定のクラスだけ避けたい場合があります。
たとえば、toStringメソッドをオーバーライドするHackも、ステートレスなクラスに対してはあまり意味を持ちません。その場合にHackを適用しないようにしたいのです。
考えられる策としては、Javaのクラスに対してHackを制御するためのTigerアノテーションを割り当ててHackの設定を書くという方法。この方法だとHack制御用アノテーションをビルドパスに含める必要がでてきて、Maven2などのEclipse以外のビルドツールの都合を考えるとで厄介です。
そこでタイプセーフじゃないしリファクタリングも利かないんですが、、、とりあえずJavadocに@suppresshackと書けばいいじゃん的発想で考えてみました。
irenkaはJavaのコードだけでなく、Javadocもirenka のDOM APIを使って読み書きできるのでその機能を使って実装してみました。以下が判定用のロジックです。isSuppressMyValidatorをHackの先頭で呼び出せはHackを適用してもよいかどうかの判定ができます。
protected boolean isSuppressMyValidator(Class<?> clazz, final CtReference ref) { List<String> validatorClasses = getSuppressHackingValue(ref); return validatorClasses.contains(clazz.getCanonicalName()); } protected List<String> getSuppressHackingValue(CtReference ref) { List<String> result = new ArrayList<String>(); CtJavadoc javadoc = ref.getJavadoc(); if (javadoc != null && javadoc.getBlocks() != null) { for (int i = 0; i < javadoc.getBlocks().size(); i++) { CtDocBlock docBlock = javadoc.getBlocks().get(i); if ("@suppresshack".equals(docBlock.getTag())) { for (int f = 0; f < docBlock.getFragments().size(); f++) { CtDocFragment docFragment = docBlock.getFragments().get(f); if (docFragment instanceof CtDocText) { CtDocText text = (CtDocText) docFragment; String paramText = text.getContent(); if (!StringUtils.isEmpty(paramText)) { String[] params = paramText.split(","); for (String param : params) { result.add(param.trim()); } } } } } } } return result; }
実際の使い方は以下。
/** toStringメソッドをオーバーライドするHack */ public class ToStringRewriter{ /** * ソースコードをリライトします。 * * @when * @param clazz */ public void rewrite(CtClass<?> clazz) { // Hackの処理を始める前に if ( isSuppressMyValidator(ToStringRewriter.class, clazz) ){ return; } // 以下、省略 } }
/** * @suppresshack com.google.code.hack.ej2.ToStringRewriter */ public class Hoge{ // suppresshackアノテーションによってHackが適用されないのでtoStringメソッドを自動生成されない }