diff --git a/build.gradle b/build.gradle index e803def..a3d721c 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ apply(plugin: 'cuba') cuba { artifact { group = 'de.balvi.cuba.declarativecontrollers' - version = "0.10.0" + version = '0.11.0' isSnapshot = false } tomcat { diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/AbstractAnnotationDispatcherBean.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/AbstractAnnotationDispatcherBean.java index adc0e35..851a465 100644 --- a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/AbstractAnnotationDispatcherBean.java +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/AbstractAnnotationDispatcherBean.java @@ -1,6 +1,7 @@ package de.balvi.cuba.declarativecontrollers.web.annotationexecutor; import com.haulmont.cuba.gui.components.Frame; +import com.haulmont.cuba.gui.screen.Screen; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -9,23 +10,22 @@ abstract public class AbstractAnnotationDispatcherBean { - protected Field[] getDeclaredFields(Frame frame) { - return frame.getClass().getDeclaredFields(); + protected Field[] getDeclaredFields(Screen screen) { + return screen.getClass().getDeclaredFields(); } - protected com.haulmont.cuba.gui.components.Component getFieldValue(Frame frame, Field field) { + protected com.haulmont.cuba.gui.components.Component getFieldValue(Screen screen, Field field) { try { field.setAccessible(true); - return (com.haulmont.cuba.gui.components.Component) field.get(frame); + return (com.haulmont.cuba.gui.components.Component) field.get(screen); } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } - - protected Annotation[] getClassAnnotations(Frame frame) { - return frame.getClass().getAnnotations(); + protected Annotation[] getClassAnnotations(Screen screen) { + return screen.getClass().getAnnotations(); } protected Collection getSupportedAnnotationExecutors(Collection potentialAnnotationExecutors, Annotation annotation) { diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenAnnotationExecutor.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenAnnotationExecutor.java new file mode 100644 index 0000000..98c311f --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenAnnotationExecutor.java @@ -0,0 +1,17 @@ +package de.balvi.cuba.declarativecontrollers.web.annotationexecutor.screen; + +import com.haulmont.cuba.gui.components.Window; +import com.haulmont.cuba.gui.screen.Screen; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.AnnotationExecutor; + +import java.lang.annotation.Annotation; +import java.util.Map; + +public interface ScreenAnnotationExecutor extends AnnotationExecutor { + + void init(A annotation, Screen screen, ScreenOptions options); + + void beforeShow(A annotation, Screen screen, ScreenOptions options); + +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenFieldAnnotationExecutor.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenFieldAnnotationExecutor.java new file mode 100644 index 0000000..9f7b66f --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/annotationexecutor/screen/ScreenFieldAnnotationExecutor.java @@ -0,0 +1,16 @@ +package de.balvi.cuba.declarativecontrollers.web.annotationexecutor.screen; + +import com.haulmont.cuba.gui.components.Component; +import com.haulmont.cuba.gui.screen.Screen; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.AnnotationExecutor; + +import java.lang.annotation.Annotation; + +public interface ScreenFieldAnnotationExecutor extends AnnotationExecutor { + + void init(A annotation, Screen screen, T target, ScreenOptions options); + + void beforeshow(A annotation, Screen screen, T target, ScreenOptions options); + +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/AnnotatableScreen.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/AnnotatableScreen.java new file mode 100644 index 0000000..7e3022d --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/AnnotatableScreen.java @@ -0,0 +1,29 @@ +package de.balvi.cuba.declarativecontrollers.web.screen; + +import com.haulmont.cuba.gui.screen.Screen; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import com.haulmont.cuba.gui.screen.Subscribe; + +import javax.inject.Inject; + +public class AnnotatableScreen extends Screen { + + @Inject + protected ScreenAnnotationDispatcher screenAnnotationDispatcher; + private ScreenOptions screenOptions; + + public AnnotatableScreen(){ + addInitListener(this::onInitEvent); + addBeforeShowListener(this::onBeforeShowEvent); + } + + public void onInitEvent(InitEvent event) { + this.screenOptions = event.getOptions(); + screenAnnotationDispatcher.executeInit(this, screenOptions); + } + + public void onBeforeShowEvent(BeforeShowEvent event) { + screenAnnotationDispatcher.executeBeforeShow(this, screenOptions); + } + +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcher.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcher.java new file mode 100644 index 0000000..5a11e81 --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcher.java @@ -0,0 +1,13 @@ +package de.balvi.cuba.declarativecontrollers.web.screen; + +import com.haulmont.cuba.gui.screen.Screen; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import de.balvi.cuba.declarativecontrollers.web.standardbrowse.AnnotatableStandardLookup; + +public interface ScreenAnnotationDispatcher { + + String NAME = "dbcdc_ScreenAnnotationExecutorService"; + + void executeInit(Screen screen, ScreenOptions screenOptions); + void executeBeforeShow(Screen screen, ScreenOptions screenOptions); +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBean.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBean.java new file mode 100644 index 0000000..24c5af7 --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBean.java @@ -0,0 +1,117 @@ +package de.balvi.cuba.declarativecontrollers.web.screen; + +import com.haulmont.cuba.core.global.AppBeans; +import com.haulmont.cuba.gui.screen.Screen; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.AbstractAnnotationDispatcherBean; +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.screen.ScreenAnnotationExecutor; +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.screen.ScreenFieldAnnotationExecutor; +import groovy.transform.CompileStatic; +import org.springframework.stereotype.Component; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collection; + +@CompileStatic +@Component(ScreenAnnotationDispatcher.NAME) +public class ScreenAnnotationDispatcherBean extends AbstractAnnotationDispatcherBean implements ScreenAnnotationDispatcher { + + @Override + public void executeInit(Screen screen, ScreenOptions screenOptions) { + executeInitUniversal(screen, screenOptions); + } + + private void executeInitUniversal(Screen screen, ScreenOptions screenOptions) { + executeInitForClassAnnotations(screen, screenOptions); + executeInitForFieldAnnotations(screen, screenOptions); + } + + private void executeInitForFieldAnnotations(Screen screen, ScreenOptions screenOptions) { + for (Field field : getDeclaredFields(screen)) { + Annotation[] fieldAnnotations = field.getAnnotations(); + + for (Annotation annotation : fieldAnnotations) { + + Collection supportedAnnotationExecutors = getSupportedScreenFieldAnnotationExecutors(annotation); + + if (supportedAnnotationExecutors != null && supportedAnnotationExecutors.size() > 0) { + com.haulmont.cuba.gui.components.Component fieldValue = getFieldValue(screen, field); + for (ScreenFieldAnnotationExecutor annotationExecutor : supportedAnnotationExecutors) { + annotationExecutor.init(annotation, screen, fieldValue, screenOptions); + } + } + + } + } + } + + private void executeInitForClassAnnotations(Screen screen, ScreenOptions screenOptions) { + for (Annotation annotation : getClassAnnotations(screen)) { + Collection supportedAnnotationExecutors = getSupportedScreenAnnotationExecutors(annotation); + + for (ScreenAnnotationExecutor annotationExecutor : supportedAnnotationExecutors) { + annotationExecutor.init(annotation, screen, screenOptions); + } + } + } + + @Override + public void executeBeforeShow(Screen screen, ScreenOptions screenOptions) { + executeBeforeShowUniversal(screen, screenOptions); + } + + private void executeBeforeShowUniversal(Screen screen, ScreenOptions screenOptions){ + executeBeforeShowForClassAnnotations(screen, screenOptions); + executeBeforeShowForFieldAnnotations(screen, screenOptions); + } + + private void executeBeforeShowForFieldAnnotations(Screen screen, ScreenOptions screenOptions) { + for (Field field : getDeclaredFields(screen)) { + + Annotation[] fieldAnnotations = field.getAnnotations(); + for (Annotation annotation : fieldAnnotations) { + + Collection supportedAnnotationExecutors = getSupportedScreenFieldAnnotationExecutors(annotation); + + if (supportedAnnotationExecutors != null && supportedAnnotationExecutors.size() > 0) { + com.haulmont.cuba.gui.components.Component fieldValue = getFieldValue(screen, field); + for (ScreenFieldAnnotationExecutor annotationExecutor : supportedAnnotationExecutors) { + annotationExecutor.beforeshow(annotation, screen, fieldValue, screenOptions); + } + } + } + } + } + + private void executeBeforeShowForClassAnnotations(Screen screen, ScreenOptions screenOptions) { + for (Annotation annotation : getClassAnnotations(screen)) { + + Collection supportedAnnotations = getSupportedScreenAnnotationExecutors(annotation); + + for (ScreenAnnotationExecutor annotationExecutor : supportedAnnotations) { + annotationExecutor.beforeShow(annotation, screen, screenOptions); + } + } + } + + + protected Collection getSupportedScreenAnnotationExecutors(Annotation annotation) { + Collection annotationExecutors = getScreenAnnotationExecutors(); + return (Collection) getSupportedAnnotationExecutors(annotationExecutors, annotation); + } + + protected Collection getSupportedScreenFieldAnnotationExecutors(Annotation annotation) { + Collection annotationExecutors = getScreenFieldAnnotationExecutors(); + return (Collection) getSupportedAnnotationExecutors(annotationExecutors, annotation); + } + + protected Collection getScreenAnnotationExecutors() { + return AppBeans.getAll(ScreenAnnotationExecutor.class).values(); + } + + + protected Collection getScreenFieldAnnotationExecutors() { + return AppBeans.getAll(ScreenFieldAnnotationExecutor.class).values(); + } +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardbrowse/AnnotatableStandardLookup.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardbrowse/AnnotatableStandardLookup.java new file mode 100644 index 0000000..89efef3 --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardbrowse/AnnotatableStandardLookup.java @@ -0,0 +1,30 @@ +package de.balvi.cuba.declarativecontrollers.web.standardbrowse; + +import com.haulmont.cuba.core.entity.Entity; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import com.haulmont.cuba.gui.screen.StandardLookup; +import com.haulmont.cuba.gui.screen.Subscribe; +import de.balvi.cuba.declarativecontrollers.web.screen.ScreenAnnotationDispatcher; + +import javax.inject.Inject; + +public class AnnotatableStandardLookup extends StandardLookup { + + @Inject + protected ScreenAnnotationDispatcher screenAnnotationDispatcher; + private ScreenOptions screenOptions; + + public AnnotatableStandardLookup(){ + addInitListener(this::onInitEvent); + addBeforeShowListener(this::onBeforeShowEvent); + } + + public void onInitEvent(InitEvent event) { + this.screenOptions = event.getOptions(); + screenAnnotationDispatcher.executeInit(this, screenOptions); + } + + public void onBeforeShowEvent(BeforeShowEvent event) { + screenAnnotationDispatcher.executeBeforeShow(this, screenOptions); + } +} diff --git a/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardeditor/AnnotatableStandardEditor.java b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardeditor/AnnotatableStandardEditor.java new file mode 100644 index 0000000..a052323 --- /dev/null +++ b/modules/web/src/de/balvi/cuba/declarativecontrollers/web/standardeditor/AnnotatableStandardEditor.java @@ -0,0 +1,30 @@ +package de.balvi.cuba.declarativecontrollers.web.standardeditor; + +import com.haulmont.cuba.core.entity.Entity; +import com.haulmont.cuba.gui.screen.ScreenOptions; +import com.haulmont.cuba.gui.screen.StandardEditor; +import com.haulmont.cuba.gui.screen.Subscribe; +import de.balvi.cuba.declarativecontrollers.web.screen.ScreenAnnotationDispatcher; + +import javax.inject.Inject; + +public class AnnotatableStandardEditor extends StandardEditor { + + @Inject + protected ScreenAnnotationDispatcher screenAnnotationDispatcher; + private ScreenOptions screenOptions; + + public AnnotatableStandardEditor(){ + addInitListener(this::onInitEvent); + addBeforeShowListener(this::onBeforeShowEvent); + } + + public void onInitEvent(InitEvent event) { + this.screenOptions = event.getOptions(); + screenAnnotationDispatcher.executeInit(this, screenOptions); + } + + public void onBeforeShowEvent(BeforeShowEvent event) { + screenAnnotationDispatcher.executeBeforeShow(this, screenOptions); + } +} diff --git a/modules/web/test/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBeanSpec.groovy b/modules/web/test/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBeanSpec.groovy new file mode 100644 index 0000000..790a545 --- /dev/null +++ b/modules/web/test/de/balvi/cuba/declarativecontrollers/web/screen/ScreenAnnotationDispatcherBeanSpec.groovy @@ -0,0 +1,175 @@ +package de.balvi.cuba.declarativecontrollers.web.screen + +import com.haulmont.cuba.gui.screen.ScreenOptions +import de.balvi.cuba.declarativecontrollers.web.annotationexecutor.screen.ScreenAnnotationExecutor +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import spock.lang.Specification + +class ScreenAnnotationDispatcherBeanSpec extends Specification { + ScreenAnnotationDispatcher screenAnnotationExecutorService + ScreenAnnotationExecutor executor + ScreenAnnotationExecutor executor2 + + def setup() { + executor = Mock() + executor2 = Mock() + + screenAnnotationExecutorService = new ScreenAnnotationDispatcherMock([executor, executor2]) + } + + void 'executeInit does nothing if the browser has no annotations'() { + + given: + AnnotatableScreen screen = Mock() + ScreenOptions screenOptions = Mock() + + when: + screenAnnotationExecutorService.executeInit(screen, screenOptions) + + then: + 0 * executor.init(_, _, _) + } + + void 'executeInit executes init if an Annotation is supported'() { + + given: + AnnotatableScreen screen = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as ToString) >> true + + when: + screenAnnotationExecutorService.executeInit(screen, screenOptions) + + then: + 1 * executor.init(_, _, _) + 0 * executor2.init(_, _, _) + } + + void 'executeInit executes init on every AnnotationExecutor that supports the Annotation'() { + + given: + AnnotatableScreen screen = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as ToString) >> true + executor2.supports(_ as ToString) >> true + + when: + screenAnnotationExecutorService.executeInit(screen, screenOptions) + + then: + 1 * executor.init(_, _, _) + 1 * executor2.init(_, _, _) + } + + void 'executeInit does nothing if there is no AnnotationExecutor for this Annotation'() { + + given: + AnnotatableScreen browse = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as EqualsAndHashCode) >> false + + when: + screenAnnotationExecutorService.executeInit(browse, screenOptions) + + then: + 0 * executor.init(_, _, _) + } + + + + + + + + + + + void 'executeBeforeShow does nothing if the browser has no annotations'() { + + given: + AnnotatableScreen screen = Mock() + ScreenOptions screenOptions = Mock() + + when: + screenAnnotationExecutorService.executeBeforeShow(screen, screenOptions) + + then: + 0 * executor.beforeShow(_, _, _) + } + + void 'executeBeforeShow executes init if an Annotation is supported'() { + + given: + AnnotatableScreen screen = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as ToString) >> true + + when: + screenAnnotationExecutorService.executeBeforeShow(screen, screenOptions) + + then: + 1 * executor.beforeShow(_, _, _) + 0 * executor2.beforeShow(_, _, _) + } + + void 'executeBeforeShow executes init on every AnnotationExecutor that supports the Annotation'() { + + given: + AnnotatableScreen screen = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as ToString) >> true + executor2.supports(_ as ToString) >> true + + when: + screenAnnotationExecutorService.executeBeforeShow(screen, screenOptions) + + then: + 1 * executor.beforeShow(_, _, _) + 1 * executor2.beforeShow(_, _, _) + } + + void 'executeBeforeShow does nothing if there is no AnnotationExecutor for this Annotation'() { + + given: + AnnotatableScreen screen = new MyBrowseWithAnnotation() + ScreenOptions screenOptions = Mock() + + and: + executor.supports(_ as EqualsAndHashCode) >> false + + when: + screenAnnotationExecutorService.executeBeforeShow(screen, screenOptions) + + then: + 0 * executor.beforeShow(_, _, _) + } +} + +class ScreenAnnotationDispatcherMock extends ScreenAnnotationDispatcherBean { + + Collection annotationExecutorCollection + + ScreenAnnotationDispatcherMock(Collection annotationExecutorCollection) { + this.annotationExecutorCollection = annotationExecutorCollection + } + + @Override + protected Collection getScreenAnnotationExecutors() { + return annotationExecutorCollection + } +} + +@ToString +class MyBrowseWithAnnotation extends AnnotatableScreen { +}