Fork us on GitHub

Properties are Amazing

I've made some improvements to the proprties API
Properties are Amazing

Properties are Amazing

I wrote about properties before but I got a sense most people didn’t understand how amazing they truly are. In this post I would like to show you something they can do that’s incredibly cool and practical for your every day usage.

To recap properties are high level attributes of an object that expose a mutable getter/setter which we can observe/introspect. I discussed these terms in the previous article so you can check there for a deeper recap. In this article I’ll show a cool demo and make it cooler with every stage!

Lets assume I have an object called Contacts which includes contact information of contact e.g.:

public class Contact implements PropertyBusinessObject {
    public final IntProperty<Contact> id  = new IntProperty<>("id");
    public final Property<String, Contact> name = new Property<>("name");
    public final Property<String, Contact> email = new Property<>("email");
    public final Property<String, Contact> phone = new Property<>("phone");
    public final Property<Date, Contact> dateOfBirth = new Property<>("dateOfBirth", Date.class);
    public final Property<String, Contact> gender  = new Property<>("gender");
    public final IntProperty<Contact> rank  = new IntProperty<>("rank");
    public final PropertyIndex idx = new PropertyIndex(this, "Contact", id, name, email, phone, dateOfBirth, gender, rank);

    @Override
    public PropertyIndex getPropertyIndex() {
        return idx;
    }

    public Contact() {
        name.setLabel("Name");
        email.setLabel("E-Mail");
        phone.setLabel("Phone");
        dateOfBirth.setLabel("Date Of Birth");
        gender.setLabel("Gender");
        rank.setLabel("Rank");
    }
}

For those of you who don’t recall something like this:

public final Property<String, Contact> name = new Property<>("name");

Is roughly equivalent to this:

private String name;

public void setName(String name) {
    this.name = name;
}
public String getName() {
    return name;
}

And you use it like this:

contact.name.set("MyName");
Log.p("My name is: " + contact.get());

So what’s so great about it?

  • Seamless persistence

  • Automatic UI Binding

  • Automatic UI generation

  • Seamless parsing

All of that is made possible because we can query information about the properties from the parent object and we can observe changes on the properties!

Seamless Serialization

Normally to make a Codename One object serialize you need to implement our Externalizable interface and do some heavy lifting. You also need to register this object so the VM will be aware of it. Codename One business objects are seamlessly externalizable and you just need to register them.

E.g. you can do something like this in your init(Object) method:

new Contact().getPropertyIndex().registerExternalizable();

After you do that once you can write/read contacts from storage if you so desire:

Storage.getInstance().writeObject("MyContact", contact);

Contact readContact = (Contact)Storage.getInstance().readObject("MyContact");

This will obviously also work for things like List<Contact> etc…​

But this gets better!

Seamless SQL Storage

I don’t like writing SQL, I’d much rather work with objects as much as possible. Which is why SQLMap is such an important API for me in the properties support. SQLMap allows CRUD (Create Read Update Delete) operations on the builtin SQLite database using property objects.

If we continue the example from above to show persistence to the SQL database we can just do something like this:

private Database db;
private SQLMap sm;
public void init(Object context) {
    theme = UIManager.initFirstTheme("/theme");
    Toolbar.setGlobalToolbar(true);
    Log.bindCrashProtection(true);

    try {
        Contact c = new Contact();
        db = Display.getInstance().openOrCreate("propertiesdemo.db"); (1)
        sm = SQLMap.create(db); (2)
        sm.setPrimaryKeyAutoIncrement(c, c.id); (3)
        sm.createTable(c); (4)
    } catch(IOException err) {
        Log.e(err);
    }
}

In the above code we do the following:

1 Create or open an SQLite database using the standard syntax
2 Create a properties binding instance
3 Define the primary key for contact as id and set it to auto increment which will give it a unique value from the database
4 Call SQL’s createTable if the table doesn’t exist yet!
Notice that at this time altering a created table isn’t possible so if you add a new property you might need to detect that and do an alter call manually

We can then add entries to the contact table using:

sm.insert(myContact);

We can update an entry using:

sm.update(myContact);

And delete an entry using:

sm.delete(myContact);

Listing the entries is more interesting:

List<PropertyBusinessObject> contacts = sm.select(c, c.name, true, 1000, 0);

for(PropertyBusinessObject cc : contacts) {
    Contact currentContact = (Contact)cc;

   // ...
}

The arguments for the select method are:

  • The object type

  • The attribute by which we want to sort the result (can be null)

  • Whether sort is ascending

  • Number of elements to fetch

  • Page to start with - in this case if we have more than 1000 elements we can fetch the next page using sm.select(c, c.name, true, 1000, 1)

There are many additional configurations where we can fine tune how a specific property maps to a column etc.

What’s Still Missing

The SQLMap API is very simplistic and doesn’t try to be Hibernate/JPA for mobile. So basic things aren’t available at this time and just won’t work. This isn’t necessarily a problem as mobile databases don’t need to be as powerful as server databases.

Relational Mappings/JOIN

Right now we can’t map an object to another object in the database with the typical one-many, one-one etc. relationships that would could do with JPA. The SQLMap API is really simplistic and isn’t suited for that level of mapping at this time.

If there is demand for this it’s something we might add moving forward but our goal isn’t to re-invent hibernate.

Threading

SQLite is sensitive to threading issues especially on iOS. We mostly ignored the issue of threading and issue all calls in process. This can be a problem for larger data sets as the calls would usually go on the EDT.

This is something we might want to fix for the generic SQLite API so low level SQL queries will work with our mapping in a sensible way.

Alter

Right now we don’t support table altering to support updated schemas. This is doable and shouldn’t be too hard to implement correctly so if there is demand for doing it we’ll probably add support for this.

Complex SQL/Transactions

We ignored functions, joins, transactions and a lot of other SQL capabilities.

You can use SQL directly to use all of these capabilities e.g. if you begin a transaction before inserting/updating or deleting this will work as advertised however if a rollback occurs our mapping will be unaware of that so you will need to re-fetch the data.

You will notice we mapped auto-increment so we will generally try to map things that make sense for various use cases, if you have such a use case we’d appreciate pull requests and feedback on the implementation.

Caching/Collision

As mentioned above, we don’t cache anything and there might be a collision if you select the same object twice you will get two separate instances that might collide if you update both (one will "win").

That means you need to pay attention to the way you cache objects to avoid a case of a modified version of an object kept with an older version.

UI Binding

One of the bigger features of properties are their ability to bind UI to a property. E.g. if we continue the sample above with the Contact class let’s say I have a text field on the form and I want the propery (which I mapped to the database) to have the value of the text field. I could do something like this:

myNameTextField.setText(myNameTextField.getText());
myNameTextField.addActionListener(e -> myContact.name.set(myNameTextField.getText());

That would work nicely but what if I changed property, that wouldn’t be reflected back into the text field?

Also that works nicely for text field but what about other types e.g. numbers, check boxes, pickers etc. this becomes a bit more tedious with those.

Binding makes this all seamless. E.g. the code above can be written as:

UiBinding uib = new UiBinding();
uib.bind(myNameTextField, myContact.name);

The cool thing is that this works with multiple component types and property types almost magically. Binding works by using an adapter class to convert the data to/from the component. The adapter itself works with a generic converter e.g. this code:

uib.bind(myRankTextField, myContact.rank);

Seems similar to the one above but it takes a String that is returned by the text field and seamlessly converts it to the integer needed by rank. This also works in the other direction…​

We can easily build a UI that would allow us to edit the Contact property in memory:

Container resp = new Container(BoxLayout.y());
UiBinding uib = new UiBinding();

TextField nameTf = new TextField();
uib.bind(c.name, nameTf);
resp.add(c.name.getLabel()). (1)
        add(nameTf);

TextField emailTf = new TextField();
emailTf.setConstraint(TextField.EMAILADDR);
uib.bind(c.email, emailTf);
resp.add(c.email.getLabel()).
        add(emailTf);

TextField phoneTf = new TextField();
phoneTf.setConstraint(TextField.PHONENUMBER);
uib.bind(c.phone, phoneTf);
resp.add(c.phone.getLabel()).
        add(phoneTf);

Picker dateOfBirth = new Picker();
dateOfBirth.setType(Display.PICKER_TYPE_DATE); (2)
uib.bind(c.dateOfBirth, dateOfBirth);
resp.add(c.dateOfBirth.getLabel()).
        add(dateOfBirth);

ButtonGroup genderGroup = new ButtonGroup();
RadioButton male = RadioButton.createToggle("Male", genderGroup);
RadioButton female = RadioButton.createToggle("Female", genderGroup);
RadioButton undefined = RadioButton.createToggle("Undefined", genderGroup);
uib.bindGroup(c.gender, new String[] {"M", "F", "U"}, male, female, undefined); (3)
resp.add(c.gender.getLabel()).
        add(GridLayout.encloseIn(3, male, female, undefined));

TextField rankTf = new TextField();
rankTf.setConstraint(TextField.NUMERIC);
uib.bind(c.rank, rankTf); (4)
resp.add(c.rank.getLabel()).
        add(rankTf);
1 Notice I use the label of the property which allows better encapsulation
2 We can bind picker seamlessly
3 We can bind multiple radio buttons to a single property to allow the user to select the gender, notice that labels and values can be different e.g. "Male" selection will translate to "M" as the value
4 Numeric bindings "just work"
Properties form for the contact
Figure 1. Properties form for the contact

Binding Object & Auto Commit

I skipped a couple of fact about the bind() method. It has an additional version that accepts a ComponentAdapter which allows you to adapt the binding to any custom 3rd party component. That’s a bit advanced for now but I might discuss this later.

However, the big thing I "skipped" was the return value…​ bind returns a UiBinding.Binding object when performing the bind. This object allows us to manipulate aspects of the binding specifically unbind a component and also manipulate auto commit for a specific binding.

Auto commit determines if a property is changed instantly or on commit. This is useful for a case where we have an "OK" button and want the changes to the UI to update the properties only when "OK" is pressed (this might not matter if you keep different instances of the object). When auto-commit is on (the default which you can change via setAutoCommit in the UiBinding) changes reflect instantly, when it’s off you need to explicitly call commit() or rollback() on the Binding class.

commit() applies the changes in the UI to the properties, rollback() restores the UI to the values from the properties object (useful for a "reset changes" button).

Binding also includes the ability to "unbind" this is important if you have a global object that’s bound to a UI that’s discarded. Binding might hold a hard reference to the UI and the property object might create a memory leak.

By using the disconnect() method in Binding we can separate the UI from the object and allow the GC to cleanup.

UI Generation

Up until now this was pretty cool but if you looked at the UI construction code above you would see that it’s pretty full of boilerplate code. The thing about boilerplate is that it shows where automation can be applied, that’s the exact idea behind the magical "InstantUI" class. This means that the UI above can be generated using this code:

InstantUI iui = new InstantUI();
iui.excludeProperty(myContact.id); (1)
iui.setMultiChoiceLabels(myContact.gender, "Male", "Female", "Undefined"); (2)
iui.setMultiChoiceValues(myContact.gender, "M", "F", "U");
Container cnt = iui.createEditUI(myContact, true); (3)
1 The id property is useful for database storage but we want to exclude it from the UI
2 This implements the gender toggle button selection, we provide a hint to the UI so labels and values differ
3 We create the UI from the screenshot above with one line and it’s seamlessly bound to the properties of myContact. The second argument indicates the "auto commit" status.

This still carries most of the flexibilities of the regular binding e.g. I can still get a binding object using:

UiBinding.Binding b = iui.getBindings(cnt);

You might not have noticed this but in the previous vebose code we had lines like:

emailTf.setConstraint(TextField.EMAILADDR);

You might be surprised to know that this will still work seamlessly without doing anything, as would the picker component used to pick a date…​

The picker component implicitly works for date type properties, numeric constraints and numbers are implicitly used for number properties and check boxes are used for booleans.

But how do we know to use an email constraint for the email property?

We have some special case defaults for some common property names, so if your property is named email it will use an email constraint by default. If it’s named url or password etc. it will do the "right thing" unless you explicitly state otherwise. You can customize the constraint for a specific property using something like:

iui.setTextFieldConstraint(contact.email, TextArea.ANY);

This will override the defaults we have in place. The goal of this tool is to have sensible "magical" defaults that "just work" so if you can think of other cases like this that make sense let us know! email?

The Code & Final Word

You can check out the code from this article here.

I could go on, last time I discussed parsing (which is also seamless to/from JSON/XML) and there are many other features worth discussing. I can also go deeper into how this is all implemented and some of the history of this feature (it predated LWUIT, I started working on this back in 2005)…​

These will have to wait for another time and post when I can go more thoroughly into them or maybe I’ll do a video covering this. We used properties a lot in the bootcamp and so far I’m very happy that I brought them in. I think we can take this feature much further than what we have above, I think this is the "tip of the iceberg" that can bring Codename One to levels of productivity/RAD that we haven’t seen. I’d love to get feedback on all of these and how we can improve properties so you can leverage them better in your apps!

Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.