New & Noteworthy

0.9.20190205.1

cancelHandler in OkCancelDialog

We added a cancelHandler to OkCancelDialog which will be called when the cancel button is invoked. To create a new OkCancelDialog with a cancelHandler, the new extended constructor can be used. Alternatively, PmoBasedDialogFactory has also been extended by a new newOkCancelDialog method that accepts a cancelHandler in addition to the okHandler.

As the new functionality further increases the number of arguments that can be passed to OkCancelDialog, a Builder is introduced to simplify the creation of a dialog. OkCancelDialog provides a new static method builder(String) that creates a new builder:

OkCancelDialog.builder(caption)
              .okHandler(okHandler)
              .cancelHandler(cancelHandler)
              .build();

The builder also supports multiple content components to be consistent with the newOkCancelDialog method in PmoBasedDialogFactory. As the OkCancelDialog should now be created using the builder, the existing constructors using a nullable content component are deprecated. A new constructor taking all configurations is provided for overriding.

New creation mechanism of aspects

The creation of linkki aspects has changed. Instead of giving the LinkkiAspectDefinition implementation class as the value for @LinkkiAspect annotation, an AspectDefinitionCreator implementation has to be used now.

This offers the benefit that the LinkkiAspectDefinitions do no longer need to depend on the corresponding annotations. The values from the annotations can be passed by the AspectDefinitionCreator. This makes the aspect definitions better testable and allows the use of the same aspect definition with multiple different annotations. The initialize() method of the LinkkiAspectDefinition, that up to now was called after creation via default constructor, is removed. The values previously read from the annotation in that method should now be read in the AspectDefinitionCreator and passed to the LinkkiAspectDefinition 's constructor, thus allowing instantiating aspect definitions from sources other than annotations.

API Changes

This feature brings several changes in the API:

  • Existing LinkkiAspectDefinitions can no longer be used in @LinkkiAspect directly. It requires an implementation of AspectDefinitionCreator to create the aspect definitions.

    Reading of the value in the AspectDefinitionCreator
    class BindTooltipAspectDefintionCreator implements AspectDefinitionCreator<@NonNull BindTooltip> {
    
        @Override
        public LinkkiAspectDefinition create(BindTooltip annotation) {
            return new BindTooltipAspectDefinition(annotation.tooltipType(), annotation.value());
        }
    }
  • initialize(Annotation) is removed from the LinkkiAspectDefinition interface. All necessary information should be directly passed to the constructor instead.
    Previously, the initialize method had the responsibility of reading the relevant attributes from the given annotation. To make the aspect definition more flexible, we recommend moving this responsibility to the creator. The constructor of the aspect definition then directly take the relevant values.

    Previous instantiation of the aspect definition with annotation
    @SuppressWarnings("null")
    private TooltipType type;
    
    @Override
    public void initialize(Annotation annotation) {
        this.type = ((BindTooltip)annotation).type();
    }
    New instantiation of the aspect definition with value
    private final TooltipType type;
    
    public BindTooltipAspectDefinition(TooltipType type) {
        this.type = type;
        this.value = value;
    }
  • If the existing LinkkiAspectDefinition extends CompositeAspectDefinition, the definition itself may not be needed any more. Instead, it can be turned into an aspect definition creator that creates a CompositeAspectDefinition in the create(Annotation) method.

    Creation of a CompositeAspectDefinition
    public class MyAspectDefinitionCreator implements AspectDefinitionCreator<@NonNull MyAnnotation> {
    
    @Override
    public LinkkiAspectDefinition create(MyAnnotation annotation) {
    return new CompositeAspectDefinition(
            new LabelAspectDefinition(annotation.label()),
            new VisibleAspectDefinition(annotation.visible()),
            new FieldValueAspectDefinition(),
            new ReadOnlyAspectDefinition());
        }
    }
  • FieldAspectDefinition has been a composite aspect definition for annotations that are annotated with @LinkkiBindingDefinition. This class is now removed as it is directly created as a composite aspect definition in FieldAspectDefinitionCreator. If you want to reuse this aspect definition, you can use the creator directly with @LinkkiAspect(FieldAspectDefinitionCreator.class) in your annotation class. Other aspects that are specific to you annotation can then be added in a separate @LinkkiAspect annotation.

Binding of aspects in ContainerPmo

The items, page length, and footer of a table are now bound to the ContainerPmo 's existing methods getItems(), getPageLength(), and getFooterPmo() using aspects. Consequently, getFooterPmo() is now bound fully dynamically. This fixes the previous bug that the footer is not removed dynamically once created.

Binding of labels as an aspect

The label is now bound using the aspect LabelAspectDefinition. If you have created a custom annotation that does not use the common FieldAspectDefinition you might need to add the new LabelAspectDefinition to your annotation.

label now mandatory in UI annotations

Our experience in multiple projects has shown that the label is specified directly with the field annotation in almost all cases. Even if the label is translated in linkki-messages.properties, it is useful to declare the label in the field annotation for documentation purposes. Therefore, the label property is now mandatory in all UI annotations except @UIButton and @UICheckBox.

Until now, there is a rarely known fallback value for the label attribute which is the capitalized property name. Thus, there was a special property noLabel to disable the label.

With this version the fallback to the property name is removed. Therefore the noLabel property is removed, too. If you do not want to show label, then simply use an empty String.

In @UIButton and @UICheckBox there was a property showLabel instead of noLabel because showing no label is the default for buttons and check boxes. Instead, both fields have a caption which is shown on top of the button or to the right of the check box. The property showLabel is removed, the default for label is still the empty String.

Default methods from interfaces in PMO classes

linkki now finds annotated methods that are inherited from interface default methods.

Custom @Bind-like annotation using Binder and fallback for pmoProperty in @Bind

The Annotation @Bind was refactored to allow creating other variations (see Custom Binding Annotation).

During this refactoring, a fallback mechanism to determine the pmoProperty attribute from the name of the annotated method (or field) has been implemented as well.

Deprecated classes

  • The methods sizedLabel(AbstractOrderedLayout, String) and sizedLabel(Layout, String, ContentMode) in the ComponentFactory have been marked as deprecated, as they duplicate the newLabelWidthUndefined methods with the same parameters which better fit the naming of the other methods in the class.

  • The classes PmoBasedTableSectionFactory and SectionCreationContext have been deprecated in favor of a unified class PmoBasedSectionFactory. You could use PmoBasedSectionFactory.createSection(Object, BindingContext) method to create all kinds of sections annotated with @UISection. If the PMO class implements ContainerPmo it automatically creates a TableSection.

  • The method BindingContext.createDispatcherChain(Object, BindingDescriptor) is deprecated. If you had overwritten this method, pass your PropertyDispatcherFactory to the new constructor BindingContext(String, PropertyBehaviorProvider, Handler, PropertyDispatcherFactory).

  • The signatures of the methods in PropertyDispatcherFactory have changed. Instead of the BindingDescriptor it directly takes the newly introduced BoundProperty which holds the reference to the bound PMO property name as well as the model object name and model object property name.