2010년 3월 5일 금요일

JDT에서 Annotation 수정하거나 추가시키기

요구사항: 클래스 Annotation 값 UI로 변경 가능하게 구현

Restful 서비스 클래스에는 아래와 같은 Annotation이 설정된다.
@javax.ws.rs.Produces("")
@javax.ws.rs.Path("")

Produces와 Path Annotation은 name-value pair로 값이 지정되는 일반 annotation이 아니라 안에 문자열로 값만을 저장하고 있는 annotation이다.
JDT에서는 이를 SingleMemberAnnotation이라 부르고, name-value pair로 값이 저장되는 것을 NormalAnnotation이라 부른다.

요구사항은 다름 아니라 Path와 Produces를 UI로 설정하면 해당 빈(클래스)에 annotation값이 변경되도록 하는 것이다.
이미 Produces나 Path annotation이 클래스에 존재하는 경우에는 StringLiteral의 문자열만 변경시켜주면 된다. 하지만 annotation이 없는 경우도 있을 테니 존재하는지 체크하는 로직과 추가시키는 로직도 포함해야 한다.

여기서도 여전히 SimpleName과 QualifiedName 문제가 있다. @javax.ws.rs.Path라고 설정하면 QualifiedName으로 @Path라고 설정하였으면 SimpleName으로 파싱된다.

createSingleMemberAnnotation 은 이미 annotation이 존재하는 경우 ASTRewrite에 의해서 replace하기 위해서 새로운 Annotation을 만드는 메서드고 findSingleMemberAnnotation 는 클래스내에 해당 Annotation이 존재하는지를 판단하는 메서드다.

Annotation을 추가(insert)하는 기능을 찾지 못하였다. 아시는 분은 댓글로 남겨 주세요. 그래서 클래스 시작 위치를 저장해 두었다가 모든 처리가 끝난 후 annotation 문자열 (ex "@javax.ws.rs.Path(\"xxx\")" 등)을 소스 코드에 추가시키도록 처리했다. 이는 모든 replace가 끝나고 document에 반영한 이후에 처리해 주어야 한다.

아래 소스에서 javaElement.getBuffer().replace(startPos, 0, sb.toString()); 부분이 소스파일에 문자열을 추가시키는 부분이다.

자세한 내용은 아래 소스를 살펴보기 바란다.



private void saveConfiguration() {
if (this.selectedBean == null) {
return;
}
String path = selectedBean.getBeanClass().replace('.', '/') + ".java";
try {


ICompilationUnit javaElement = (ICompilationUnit) javaProject
.findElement(new Path(path));
if (javaElement == null || javaElement.exists() == false) {
return;
}

Document document = new Document(javaElement.getSource());

CompilationUnit astRoot = ProjectHelper.parse(javaElement);


astRoot.recordModifications();

ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST());
TypeDeclaration typeDecl = (TypeDeclaration) astRoot.types().get(0);

int startPos = typeDecl.getStartPosition();

SingleMemberAnnotation pathAnno = findSingleMemberAnnotation(
typeDecl, "Path", "javax.ws.rs.Path");


String ln = System.getProperty("line.separator");
StringBuffer sb = new StringBuffer();

if (pathAnno != null) {
SingleMemberAnnotation newPathAnno = createSingleMemberAnnotation(
astRoot, "javax.ws.rs.Path", pathText.getText());
rewrite.replace(pathAnno, newPathAnno, null);
} else {
sb.append("@javax.ws.rs.Path(\"").append(pathText.getText()).append("\")").append(ln);
}

SingleMemberAnnotation producesAnno = findSingleMemberAnnotation(
typeDecl, "Produces", "javax.ws.rs.Produces");

if (producesAnno != null) {
SingleMemberAnnotation newProducesAnno = createSingleMemberAnnotation(
astRoot, "javax.ws.rs.Produces", producesText.getText());
rewrite.replace(producesAnno, newProducesAnno, null);
} else {
sb.append("@javax.ws.rs.Produces(\"").append(producesText.getText()).append("\")").append(ln);
}


TextEdit edits = rewrite.rewriteAST(document, javaElement
.getJavaProject().getOptions(true));
edits.apply(document);


javaElement.getBuffer().setContents(document.get());

if(sb.length() > 0) {
javaElement.getBuffer().replace(startPos, 0, sb.toString());
}

javaElement.save(null, true);


} catch (Exception e) {
DialogHelper.showError(getShell(), e.getMessage());
AmfPlugin.log(e);
}
}

private static SingleMemberAnnotation createSingleMemberAnnotation(
CompilationUnit astRoot, String annotationFullyQualifiedName,
String literalValue) {
SingleMemberAnnotation newAnno = null;
newAnno = astRoot.getAST().newSingleMemberAnnotation();

String[] array = annotationFullyQualifiedName.split("\\.");
if (array.length < 1) {
return null;
}
Name typeName = astRoot.getAST().newName(array);
newAnno.setTypeName(typeName);
StringLiteral annoValue = astRoot.getAST().newStringLiteral();
annoValue.setLiteralValue(literalValue);
newAnno.setValue(annoValue);

return newAnno;
}

public static SingleMemberAnnotation findSingleMemberAnnotation(
TypeDeclaration typeDecl, String simpleName,
String fullyQualifiedName) {
List list = (List) typeDecl
.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY);

for (Object obj : list) {
if (obj instanceof SingleMemberAnnotation) {
SingleMemberAnnotation cast = (SingleMemberAnnotation) obj;
Name typeName = cast.getTypeName();
if (simpleName.equals(typeName.getFullyQualifiedName())
|| fullyQualifiedName.equals(typeName
.getFullyQualifiedName())) {
return cast;
}
}
}
return null;
}


댓글 1개:

  1. MethodDeclaration.modifires().add(어노테이션) 이렇게 어노테이션을 추가할 수 있습니당 ㅎㅎ

    답글삭제