Nie nudząc technicznymi szczegółami przejdźmy do przykładu. Klasę np. Order oznaczamy adnotacją @GenerateBuilder, która informuje procesor, że dla niej ma zostać wygenerowany builder:
@GenerateBuilder public class Order { private List<OrderItem> items; private Date createDate; private boolean realized; public boolean isRealized() { return realized; } public void orderRealized() { // very complex implementation realized = true; } }Żeby w jakiś sposób automatycznie aktualizować istniejące buildery o nowe pola, które doszły w klasie Order (lub odpowiednio usuwać metody inicjujące nieistniejące pola), builder został rozbity na 2 klasy. Jedna z nich jest aktualizowana zawsze przy uruchomieniu procesora, druga generowana tylko raz, dzięki czemu możemy do niej dodawać własne metody budujące. Dla klasy Order powstaną następujące klasy:
public abstract class AbstractOrderBuilder<B> extends AbstractBuilder<Order, B> { public abstract B withItems(List<OrderItem> items); public abstract B withCreateDate(Date createDate); public abstract B withRealized(boolean realized); public B withItems(OrderItem... items){ return withItems(new ArrayList<OrderItem>(Arrays.asList(items))); } }
public abstract class OrderBuilder extends AbstractOrderBuilder<OrderBuilder> { public static OrderBuilder anOrder(){ return AbstractBuilderFactory.createImplementation(OrderBuilder.class); } }Samo użycie może wyglądać następująco:
@Test public void shouldCreateRealizedOrder() { // when Order order = anOrder().withRealized(true).build(); // then assertTrue(order.isRealized()); }Klasę OrderBuilder można rozszerzać, wywołując rzeczywiste metody domenowe, np:
public abstract class OrderBuilder extends AbstractOrderBuilder<OrderBuilder> { public static OrderBuilder create() { return AbstractBuilderFactory.createImplementation(OrderBuilder.class); } public OrderBuilder realized() { Order order = targetObject(); // invoking real domain method order.orderRealized(); // other methods return builder(); } }
@Test public void shouldCreateRealizedOrder() { // when Order order = anOrder().realized().build(); // then assertTrue(order.isRealized()); }Do podłączenia procesora do projektu może zostać wykorzystany ant, maven, eclipse. Jest również możliwość użycia generatora w starym stylu, czyli wygenerowania ciała klasy buildera do konsoli.
Procesor może również poszukiwać klas z adnotacjami JPA (@Entity, @Ebeddable, @MappedSuperclass).
Do poznania szczegółów odsyłam do wiki projektu. Zachęcam do forkowania i dzielenia się uwagami.
A może tak: http://projectlombok.org/
OdpowiedzUsuńNie widzę związku, lombok jedyne co robi, to generuje za Ciebie gettery i settery (plus kilka innych metod)?
UsuńCiekawy projekt. Ostatnio też bawię się z generatorami kodu opartymi o Annotation Processing Tool.
OdpowiedzUsuńTak więc przyda mi się twa praca w poszukiwaniu inspiracji w alternatywnym podejściu do tego.
Chyba jest błąd w klasie ProcessorContext, zamist:
public static final String FLUENT_BUILDER_ANNOTATATION = "info.ludwikowski.processor.GenerateBuilder";
Powinno być:
public static final String FLUENT_BUILDER_ANNOTATATION = "info.ludwikowski.annotation.GenerateBuilder";
Wynika to zapewne z tego że w anotacjach nie można byłoby użyć tej konstrukcji jeśli byłoby to wyrażone tak:
public static final String FLUENT_BUILDER_ANNOTATATION = GenerateBuilder.class.getName();
Ciekawe czy użycie, właśnie Annotation Processing Tool do gerowania słownika takich zmiennych nie byłoby dobrym rozwiązaniem do obejścia tego problemu?
Ciekawi mnie też jak pracowałeś z api z pakietów "javax.lang.model"?, z własnego doświadczenia wiem że debugowanie tego kodu może być uciążliwe.
Dzięki za uwagę, robiłem przepakietowanie żeby można było tego w pracy używać w kontenerze osgi, poprawiłem 1.0.6. Co dziwne działało bez problemu:)
UsuńJeśli chodzi o debugowanie to jedną opcją jest opakowanie tego "pluginem" do eclipsa, brzmi dość strasznie, ale nie jest to aż takie trudne. Wzorowałem się na tych materiałach:
http://kerebus.com/2011/02/using-java-6-processors-in-eclipse/
https://code.google.com/p/acris/wiki/AnnotationProcessing_DebuggingEclipse
http://vimeo.com/8876665
Dzięki za linki. Szczególnie ten ostatni jest pomocny. Jak już się eclispa uruchomi, to
Usuńnie trzeba restartować go z każdą zmianą - to jest zwłaszcza pomocne przy poznawaniu tego api.
Ja znalazłem w sieci podejście z zastosowaniem kompilacji podczas testów, co też jest pomocne.
Wzorowałem się na tym przykładzie:
https://svn.java.net/svn/glassfish~svn/trunk/logging-annotation-processor/src/test/java/org/glassfish/logging/annotation/LogMessagesResourceBundleGeneratorTest.java
She wasn’t 카지노사이트 the first to recognize this vulnerability and capitalize on it. A month later, I met with a girl infamous for having won more than $20 million from casinos in lower than five years. Elite Western gamblers and rattled on line casino bosses know her as Kelly, a fast-talking, sunglasses-wearing benefit player in her 40s.
OdpowiedzUsuń