Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

...

A bound property in a Java bean is a field which sends notifications to listeners when it is changed. This means it is made up of these parts:

  • There is a list of listeners who are interested in changes
  • The field itself
  • Methods to add and remove listeners
  • Setter code which changes the value and notifies the listeners if the value has changed

Example 2: Merge Java and SQL

OR Mappers will only get you so far. While they will solve many or all problems, they also introduce new ones:

  • You have to learn to use the mapper
  • You must still understand how a database works
  • The mapper will try to connect the Java semantics to the Database semantics. This is not always straightforward. For example, a table might define a non-unique primary key. There is no way to map this to a Java map where primary keys must be unique or you will overwrite objects.
  • When a mapper doesn't support a specific corner case, you're in trouble. Mappers are often quite monolithic and they shove their tendrils in many places. Usually, you can't split it apart, injecting your own code in certain places.

So what do we expect from AST Macros in this case?

  • They shouldn't get in the way of the developer. If she choses to use an OR Mapper, she should be free to do so.
  • It should help to manage all the cases which the OR Mapper doesn't handle well.
  • It should give aid to interface the Groovy code with the OR Mapper.
  • It should allow to write code which directly interfaces the DB (for example, when you have to execute some special SQL which the OR Mapper simply can't do).

Some simple examples:

  • counting rows
  • loading objects from a DB

Examples

Before we look at solutions, let's look at what the code ought to do in the end.

...

Code Block
import javax.beans.*;

class A {
    // the following is added only once per class
    PropertyChangeSupport propertyChangeSupport

    void addPropertyChangeListener(PropertyChangeListner listner) {
        propertyChangeSupport.addPropertyChangeListner(listener)
    }

    void addPropertyChangeListener(String property, PropertyChangeListner listner) {
        propertyChangeSupport.addPropertyChangeListner(property, listener)
    }

    void removePropertyChangeListener(PropertyChangeListner listner) {
        propertyChangeSupport.removePropertyChangeListner(listener)
    }

    void removePropertyChangeListener(String property, PropertyChangeListner listner) {
        propertyChangeSupport.removePropertyChangeListner(property, listener)
    }

    PropertyChangeSupport[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners
    }

    // the following is added per each annotated proeprty
    private int x

    void setX (int x) {
    	propertyChangeSupport.firePropertyChanged('x', this.x, this.x = x)
    }

    int getX() {
        return x;
    }
}

specifically:

  • When using code completion, the additional fields and methods should be visible.
  • The added code should be lean and fast
  • It should not get in the way of existing user code, for example, it should be possible to define a custom setter which gets wrapped by the code above
  • If a custom setter exists, it should be possible to invoke it before the comparison (so you can define setters which convert the value before assigning it)

This leads to a couple of demands which an AST Macro Processor (AMP) must met:

  • It must be able to see all fields and methods, no matter if the user supplied them or an AMP added them.
  • It must be able to add new methods and fields and static code to an existing class and presumably also to classes created by an AMP
  • It must be able to reorder source code or at least AST Nodes.

In a perfect world, an AMP should be able to modify the code on a source level and pass it back to an IDE, for example, so that I can see (and debug) what is actually compiled (instead of only seeing the Annotation).

...

For Groovy-specific macros, the solution is to add a marker interface to the macro annotation.

Options

  • Add a config file to the compiler
  • Users have to use a different annotation which implements both Groovy's marker interface and javax.persistence.Entity

Links