четверг, 31 октября 2013 г.

Custom Annotation Preprocessor — создание на базе Android-приложения и конфигурация в IntelliJ IDEA

Всем привет!

Недавно передо мной встала задача написания своих кастомных аннотации и их обработки во время компиляции. Первый вопрос, который я себе задала: с чего начать? После анализа я решила поделиться с вами ответом на этот вопрос.

Думаю, рассказывать, что такое аннотации в java и с чем их едят, не имеет смысла, так как каждому юному программисту это знакомо ( а кому не знакомо, может прочесть самостоятельно). К тому же на хабре есть интересная ознакомительная статья об этом явлении.

Но сегодня я хочу поговорить именно о кастомных аннотациях в Android-приложении, которые обрабатываются в процессе компиляции проекта вашим собственным обработчиком и о автогенерации классов на их основе. А так же, по ходу дела, расскажу вам, как быстро все настроить в IDEA (сама я пользуюсь версией 12.1, возможно в других есть отличия).



Для реализации нам понадобится 2 проекта: в первом опишем наши кастомные аннотации и их обработчик, из него сгенерируем jar файл, который подключим ко второму тестовому проекту, который и будет использовать наши аннотации.


Шаг 1



Создаем новый library проект, в котором описываем свою аннотацию. Класс с аннотацией выглядит примерно так:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface CustomAnnotation {

String className();
String value() default "Hello";
int type() default 0;
}


@Retention говорит о том, что наша аннотация будет присутствовать только в исходном коде и отброшена компилятором (а до этого момента мы её обработаем).


Шаг 2



Создаем непосредственно наш обработчик аннотаций. Он представляет собой класс, расширяющий AbstractProcessor. Ему мы говорим, что он будет обрабатывать все аннотации и указываем поддерживаемую версию исходных файлов таким образом:

@SupportedAnnotationTypes({"*"})
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class CustomProcessor extends AbstractProcessor {
}




Далее переопределяем метод process, в котором прописываем логику генерации нового класса. Мой метод выглядит так:

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

for (Element e : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
CustomAnnotation ca = e.getAnnotation(CustomAnnotation.class);
String name = e.getSimpleName().toString();
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
name = new String(name);
TypeElement clazz = (TypeElement) e.getEnclosingElement();
try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Autogenerate");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
try {
String pack = clazz.getQualifiedName().toString();
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ pack.substring(0, pack.lastIndexOf('.')) + ";");
pw.println("\npublic class "
+ clazz.getSimpleName() + "Autogenerate {");

TypeMirror type = e.asType();

pw.println("\n public " + ca.className() + " result = \"" + ca.value() + "\";");

pw.println(" public int type = " + ca.type() + ";");


pw.println("\n protected " + clazz.getSimpleName()
+ "Autogenerate() {}");
pw.println("\n /** Handle something. */");
pw.println(" protected final void handle" + name
+ "(" + ca.className() + " value" + ") {");
pw.println("\n//" + e);
pw.println("//" + ca);
pw.println("\n System.out.println(value);");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
return true;
}




На этом этапе можно включить фантазию и писать, что душе угодно (ну или что требуется =). После того, как вы закончили с обработчиком аннотаций и описали все свои кастомные аннотации, из этого проекта нужно сгенерировать jar файл. В Idea 12 это делается достаточно просто: Project Settings -> Artifacts -> Add -> Jar -> From modules… Далее делаем Build -> Rebuild Project и находит наш сгенерированный файл jar в Output директории проекта.
Шаг 3



Создаем тестовый проект, в котором и будем использовать наши кастомные аннотации. К проекту подключаем сгенерированный на прошлом шаге jar файл и радуемся, что наши аннотации теперь нам доступны. В любом классе прописываем нашу аннотацию, например так:

public class MyActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@CustomAnnotation(className = "String", type = 1)
public void annotatedMethod(String value) {
}
}




Помните, что мы указали нашей аннотации @Target(ElementType.METHOD), а это значит что мы можем прописать ее только перед методом.
Шаг 4



Теперь скажем Idea использовать наш обработчик аннотаций и магия начнет работать! Для этого идём в Settings в раздел Compiler — > Annotation Processors. Ставим галочку Enable annotation processing, в поле Processor path указываем путь к нашему сгенерированному jar файлу. Так же в окне Processor FQ name вводим полное название класса, который отвечает за обработку. В нашем случает это CustomProcessor. Заполненное окно должно выглядеть примерно так.


Шаг 5



Делаем Build -> Rebuild project и наслаждаемся результатами. В дереве проекта должна появиться папка generated, в которой будут лежать новые файлы.

Вот что получилось у меня:

public class MyActivityAutogenerate {

public String result = "Hello";
public int type = 1;

protected MyActivityAutogenerate() {}

/** Handle something. */
protected final void handleannotatedMethod(String value) {

//annotatedMethod(java.lang.String)
//@com.example.AnnotationsProcessor.CustomAnnotation(type=1, value=Hello, className=String)

System.out.println(value);
}
}


Happy codding!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий