Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Why typed properties?

One of the banes of Java is the abomination we call properties: the sad convention that gives us getters, setters and strings to access them. Using strings to access properties is a disaster because with one fell sweep all that work that the compiler does to check everything is useless: using a string means all errors will occur at runtime. And forget about refactoring: renaming properties is an exercise in frustration as even with the high level of support for refactoring in IDE's property references in strings will still bring your code down.

Languages like Kotlin do have properties and -more important- they have property references. These are typeful entities that are fully checked during compilation, and these are fully visible to the IDE too, so renames will work without issues. But all is not yet well: Kotlin property support still lacks some important things:

  • Kotlin property references only work for classes made in Kotlin. A class defined in Java has no property references. This means that in this respect here is no interoperability at all: to use property references the classes used must be written in Kotlin.
  • You can reference a single property but there is no nice way to reference a property path: something like Invoice::debtor::address::city or similar. This makes it hard to have path like structures. There are a few workarounds but most of them suck.

Since our Java "architects" are still busy with botching the things C# did well years ago we can probably forget about ever getting something reasonable, so we need a workaround.

Many frameworks have the above problem, so there are many solutions. The JPA 2.0 Criteria API has a specification for a domain class processor where an annotation processor is used to generate typed properties for all Entity classes in the JPA model. Libraries like queryDSL have a similar approach where a processor or a Maven plugin generates special classes for your entities, allowing you to write SQL in a reasonably intuitive and typesafe way. The problem with all of these is that they service a very specific area of the problem: they are defined for database entities. But any Java class that is part of a model can need typed properties, not just database related classes. So we need something more "generic".

Using typed properties in code

In code you use typed properties at all locations where you would usually use  a string containing some property path. For example:

...

Summary

Typed Properties in DomUI are classes that are generated automatically, using an Annotations Processor, much like the JPA Static Metamodel - but for any Java class that exposes getters and setters.

Once configured you can address properties in code not by a String, like "definition.code", but as a fully typed and compile-time checked path like Fact_.definition().code(). You can create typed properties for any class, not just for database classes. And because the properties are fully typed the code using Typed Properties alsoknows what is the type of a property - allowing the compiler yet again to check more at compile time.

Most DomUI code that works/accepts properties accepts both the "old" String variants but also the typed properties. And there is an IntelliJ Idea plugin which automatically replaces String references to properties to the equivalent typed properties.

Why typed properties?

One of the banes of Java is the abomination we call properties: the sad convention that gives us getters, setters and strings to access them. Using strings to access properties is a disaster because with one fell sweep all that work that the compiler does to check everything is useless: using a string means all errors will occur at runtime. And forget about refactoring: renaming properties is an exercise in frustration as even with the high level of support for refactoring in IDE's property references in strings will still bring your code down.

Languages like Kotlin do have properties and -more important- they have property references. These are typeful entities that are fully checked during compilation, and these are fully visible to the IDE too, so renames will work without issues. But all is not yet well: Kotlin property support still lacks some important things:

  • Kotlin property references only work for classes made in Kotlin. A class defined in Java has no property references. This means that in this respect here is no interoperability at all: to use property references the classes used must be written in Kotlin.
  • You can reference a single property but there is no nice way to reference a property path: something like Invoice::debtor::address::city or similar. This makes it hard to have path like structures. There are a few workarounds but most of them suck.

Since our Java "architects" are still busy with botching the things C# did well years ago we can probably forget about ever getting something reasonable, so we need a workaround.

Many frameworks have the above problem, so there are many solutions. The JPA 2.0 Criteria API has a specification for a domain class processor where an annotation processor is used to generate typed properties for all Entity classes in the JPA model. Libraries like queryDSL have a similar approach where a processor or a Maven plugin generates special classes for your entities, allowing you to write SQL in a reasonably intuitive and typesafe way. The problem with all of these is that they service a very specific area of the problem: they are defined for database entities. But any Java class that is part of a model can need typed properties, not just database related classes. So we need something more "generic".

Using typed properties in code

In code you use typed properties at all locations where you would usually use  a string containing some property path. For example:

Code Block
List<Album> query = dc().query(QCriteria.create(Album.class).eq(Album_.artist().name(), "AC/DC"));
System.out.println("Got " + query.size() + " results");

...

Code Block
bedragTypeCombo.bind(ComboFixed.READONLY).to(model(), LineController_.readOnly());

Enter the typed properties annotation processor.

Because having just one wheel is not that useful: let's invent yet another one. The DomUI property-annotations-processor is an annotation processor which will create special classes for each class that we use in a model. It generates only classes with typeful properties; it does not add "natural query" ability or whatever.

To use the annotations processor in Maven you need to add the annotations processor to your pom, and you need to add it to each project that has classes that you want to have typeful properties for. Add the following to the project's POM if you have a single project:

...

Finally, using typed properties makes it very easy to work with binding converters. These are used when the source of a binding has a different type from the target. Take the following example:

Code Block
Text2<BigDecimal> ctrl = new Text2<>(BigDecimal.class);
ctrl.setConverter(new MoneyBigDecimalFullConverter());
ctrl.bind("readOnly").to(model(), LineController_.readOnly());
ctrl.bind(NodeBase.VISIBILITY).to(row, Line_.amountType(), amountType -> amountType == AmountType.Amount ? VisibilityType.VISIBLE : VisibilityType.HIDDEN);

The last line binds the CSS "visibility" property to the amountType property of the model, and ensures that the control is only visible when the amountType is Amount. We can use a lambda as above because the types of both from and to are known; had we used properties strings then we would have needed to write a complicated cast on the bind code.

Enter the typed properties annotation processor.

Because having just one wheel is not that useful: let's invent yet another one. The DomUI property-annotations-processor is an annotation processor which will create special classes for each class that we use in a model. It generates only classes with typeful properties; it does not add "natural query" ability or whatever.

To use the annotations processor in Maven you need to add the annotations processor to your pom, and you need to add it to each project that has classes that you want to have typeful properties for. Add the following to the project's POM if you have a single project:

Code Block
<build>
    <plugins>
        <plugin>
           <dependency>
 <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-compiler-plugin</artifactId>
           <groupId>to.etc.domui</groupId> <version>${maven-compiler-plugin.version}</version>
            <configuration>
            <artifactId>property-annotations-processor</artifactId>    <annotationProcessors>
                    <version>1.2-SNAPSHOT</version>
 <annotationProcessor>db.annotationprocessing.PropertyAnnotationProcessor</annotationProcessor>
                  </dependency>annotationProcessors>
                </annotationProcessorPaths><annotationProcessorPaths>
            </configuration>        <dependency>
       <dependencies>                 <dependency><groupId>to.etc.domui</groupId>
                        <groupId>to.etc.domui</groupId><artifactId>property-annotations-processor</artifactId>
                        <artifactId>property-annotations-processor</artifactId><version>1.2-SNAPSHOT</version>
                    <version>1.2-SNAPSHOT</version></dependency>
                </dependency>annotationProcessorPaths>
            </dependencies>configuration>
        </plugin>
    </plugins>
</build>

...

If you have a project that consists of multiple modules then you should add the above configuration to the existing maven-compiler-plugin definition inside your parent/root POM, so that all projects get the configuration automatically. In such a case the config might also be present in a pluginManagement section.

...

You also need to use at least the following versions of the build plugins specified in your parent/top pom:

PluginVersion
maven-compiler-plugin3.7.0
plexus-compiler-eclipse2.8.4Only when you build your code in Maven using the Eclipse plugin, see this stackoverflow article for details



Warning

One warning: if you use the Eclipse compiler to compile your code in Maven then you need to make sure that you use at least version 2.8.4 of the plexus-compiler-eclipse plugin (to be released) because any version before that does not support annotation processors.

Extra configuration when using IntelliJ

IntelliJ does not really see the Annotations Processor as a "dependency" of the project. So if you are using DomUI as as submodule (so as source code) then IntelliJ will complain that the annotation processor could not be found. This is because no project "depends" on the annotation processor as far as IntelliJ is concerned - so it does not build it.

To fix this just add the annotation processor as a direct dependency of all of the modules that need the thing inside the project that needs it by adding the following fragment to the dependencies section:

Code Block
                    <dependency>
                        <groupId>to.etc.domui</groupId>
                        <artifactId>property-annotations-processor</artifactId>
                        <version>1.2-SNAPSHOT</version>
                    </dependency>



Using the annotations processor with IntelliJ and Maven

...

Code Block
<dependency>
  <groupId>to.etc</groupId>
  <artifactId>common<<artifactId>annotations</artifactId>
  <version>1.2-SNAPSHOT</version>
</dependency>

...