かとじゅんの技術日誌

技術の話をするところ

Javadocのコメントで強制的にHackの適用をOFFにする方法

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メソッドを自動生成されない
}