Get the book!
The best Java IDE

Renderers and Editors for Atomic Types

Posted by Eitan Suez Thu, 24 Aug 2006 20:46:00 GMT

Let's take a look at an example model object, say a Person:

  public class Person
        extends AbstractComplexEObject
  {
     private final StringEO name = new StringEO();
     private final TextEO bio = new TextEO();
     private final DateEO birthDay = new DateEO();
     private final ImgEO picture = new ImgEO();
     ...

We see four properties are defined: the person's name, bio, birthday, and their picture.

Here is the corresponding Form View in JMatter:

New Person Form View

JMatter uses a different editor for each property. For every atomic type, such as a StringEO, a DateEO, or an SSN, there exists a corresponding renderer and editor that JMatter uses to, well, render and edit each property.

Let's explore the contract for writing renderers and editors for atomic types.

JMatter defines two interfaces: AtomicRenderer and AtomicEditor:

  public interface AtomicRenderer
  {
     public void render(AtomicEObject value);
  }

  public interface AtomicEditor extends AtomicRenderer
  {
     public int bind(AtomicEObject value);
  }

Here is JMatter's actual implementation of AtomicRenderer for rendering StringEO's:

  public class StringRenderer extends JLabel implements AtomicRenderer
  {
     public void render(AtomicEObject value)
     {
        setText(value.toString());
     }
  }

With very little explanation, we readily understand this contract: JMatter will ask renderers to render a piece of information, by calling the render() method and passing it the value in question. Our implementation here uses a JLabel and its setText() method is used to display the information.

Let's take a look at an implementation of an AtomicEditor:

  public class StringEditor extends JTextField implements AtomicEditor, ActionNotifier
  {
     public StringEditor() { super(12); }

     public void render(AtomicEObject value)
     {
        if (value.field() != null && value.field().displaysize() > 0
              && value.field().displaysize() != getColumns())
        {
           setColumns(value.field().displaysize());
        }

        StringEO eo = (StringEO) value;
        if (!getText().equals(eo.stringValue()))
           setText(eo.stringValue());
     }
     public int bind(AtomicEObject value)
     {
        StringEO eo = (StringEO) value;
        eo.setValue(getText());
        return 0;
     }
  }

In this case, the editor extends a JTextField and the editor is sensitive to JMatter's metamodel. That is, I've recently defined a new piece of meta-information on fields, which can be used to control the editor's text field's length (displaysize()). The AtomicEditor interface extends the AtomicRenderer's interface, so both methods, render() and bind() must be implemented.

The bind() method is relatively easy to understand: the framework will pass the editor a model object, and the editor's job is to bind its editor's value to the model object. So in this case, we retrieve the JTextField's value (with getText()) and invoke StringEO.setValue() to do the binding.

The contract with bind has one additional requirement: the editor has the ability to veto the action if some kind of validation error took place. Take a look at USZipEditor for a moment:

  public int bind(AtomicEObject value)
  {
     try
     {
        value.parseValue(getText());
        return 0;
     }
     catch (java.text.ParseException ex)
     {
        value.fireValidationException(ex.getMessage());
        return 1;
     }
  }

Here, the USZipEditor asks a USZipCode model object to try to parse the text. If the text contains invalid characters, a ParseException is caught, and we're returning a one (1): the number of validation errors encountered during the binding attempt. So we see that a zero return value indicates an "all's well" condition.

Finally, the binding of renderers to types is determined by the implementation of the interface ViewMechanism, which among other methods, defines methods such as these:

  public AtomicRenderer getStringRenderer();
  public AtomicEditor getStringEditor();

  public AtomicRenderer getTextRenderer();
  public AtomicEditor getTextEditor();

  public AtomicRenderer getSSNRenderer();
  public AtomicEditor getSSNEditor();

  public AtomicRenderer getDateRenderer();
  public AtomicEditor getDateEditor();

  public AtomicRenderer getImageRenderer();
  public AtomicEditor getImageEditor();
  ...

Taking a peek at JMatter's SwingViewMechanism implementation, we see the bindings:

  public AtomicRenderer getStringRenderer()
  {
     return new StringRenderer();
  }
  public AtomicEditor getStringEditor()
  {
     return (AtomicEditor) borrowObject(StringEditor.class);
  }

In summary, the mechanism for writing renderers and editors is clean and simple. It should be trivial for anyone to write beautiful, custom renderers and editors for all the atomic types supported by JMatter (and the list is long). Adding new atomic types is also fairly straightforward, and hopefully will be the subject of a future blog entry.

Now that you know how to write your own renderers and editors for JMatter, you have the freedom to customize JMatter in that respect for any applications you're developing.

And if by any chance you develop editors that are superior to the existing implementations, consider submitting your implementations as candidates to become the default implementation in future JMatter releases.

no comments

Tip of the week: Validation

Posted by Eitan Suez Sat, 12 Aug 2006 01:27:00 GMT

Shedding Light on JMatter's Design. Part 2: Validation

In this Blog entry I will describe how validation works in JMatter. There are a number of facets to validation.

Let's begin by describing one aspect of the GUI. In the user interface, when an object is displayed, it can be edited and saved (updated). The user interface code that lays out the form conveniently inserts a number of panels called validation panels. These panels are normally empty and thus not apparent in the user interface.

For an object with three atomic fields, say a Speaker with a name (StringEO), a title (StringOE), and a biography (TextEO), four validation panels will be setup: one for each field, and one for the instance.

Each atomic type asserts its own validation requirements. Let's take a look at USZipCode for a moment:

  private static String omit = "- ";
  private static String valid = "0123456789";

  public int validate()
  {
     String value = SimpleParser.parseValue(omit, valid, _value);
     if (value == null || value.length() != 5 && value.length() != 9) return invalid();
     return 0;
  }

  private int invalid()
  {
     fireValidationException("Invalid zip code: "+_value);
     return 1;
  }

Here we see that USZipCode implements the validate() method, which the framework will invoke at the proper time. The return value, an int, specifies the number of "errors" encountered. So a return value of 0 implies that everything validated just fine.

There's a publish/subscribe pattern here, where the model object does not have a reference to its corresponding view (or editor) object. Yet, somehow the UI must display the validation error message. So the model object publishes the message Invalid zip code... when a validation error is encountered. The validation panel that corresponds to a field of this type in the user interface is a listener, and receives the messages, and properly displays the error:

Zip Code Validation

The way you should view this form is that each field has its own validation panel embedded in the form, just above it. Most of the time these panels are dormant. When a validation message is published, it will appear in the appropriate place. Each panel listens to the value of its corresponding field.

The method validate() will be called on atomic objects when a user tabs out of the editor (when the field loses focus) and again when the user attempts to save the object.

So validation on atomic types such as such as USZipCode, SSN, and USPhone is implemented in this way.

Some validation logic is really more an artifact of how the atomic type's editor chose to allow you to enter the information, and so is implemented directly on the editor. Let's take a look at PasswordEditor's bind() method, which is called by the framework to bind the value entered in the editor back to the model object:

  public int bind(AtomicEObject value)
  {
     Password eo = (Password) value;

     String pwd1 = new String(_pf1.getPassword());
     String pwd2 = new String(_pf2.getPassword());

     if (pwd1.length() < Password.MINLENGTH)
     {
        eo.fireValidationException("Password must be at least "+Password.MINLENGTH+" characters long");
        return 1;
     }

     if (! pwd1.equals(pwd2) )
     {
        eo.fireValidationException("Password and repeated password do not match.");
        return 1;
     }

     eo.parseValue(pwd1);
     return 0;
  }

This editor displays two password fields. The entered value must match each time. Also there's a check to ensure that the specified password meets certain criteria, such as a minimum length, or a exceeding a certain minimum password strength, based on a specific algorithm. Again, we see here how the editor, through its reference to the model object, can also tap into the validation panels to display the proper validation error message.

In addition, baked into the framework is a simple mechanism for marking fields as required. The framework will automatically check that required fields left empty will veto the ability to save an object, and will also display a proper error message.

The way to mark required fields in JMatter at the moment is to specify them in the properties file model-metadata.properties. Here's a sample file, taken from an application having to do with aircraft that I recently implemented:

  Airport.airportID.required=true
  Airport.name.required=true
  Airport.lat.required=true
  Airport.lon.required=true
  Airport.city.required=true
  Airport.stateCode.required=true

  Trip.aircraft.default=from Aircraft a where name='CitationJet'

  TripSegment.from.required=true
  TripSegment.to.required=true

Notice that this metadata file is also used to specify default values for fields. Note how a default value is specified for an association by actually looking it up in the database. The value specified is valid hql (hibernate query language). Let's get back to discussing validation.

Any type you define in your object model can define its own validation as well, again, simply by implementing validate(). Here's how it was implemented for the type "TripSegment" in an application I developed:

  public int validate()
  {
     if (_from == null || _to == null)
     {
        String msg = "From and To fields cannot be empty";
        fireValidationException(msg);
        return 1;
     }
     if (_from.equals(_to))
     {
        fireValidationException("From and To fields cannot be the same");
        return 1;
     }
     if (_trip != null && _trip.getAircraft() != null)
     {
        Aircraft aircraft = _trip.getAircraft();
        if (aircraft.hasRangeFor(_numPassengers.intValue()))
        {
           int range = aircraft.rangeFor(_numPassengers.intValue());
           if (range < distance())
           {
              String msg = String.format("Segment distance exceeds aircraft range (%d) " +
                    "for specified number of passengers", range);
              fireValidationException(msg);
              return 1;
           }
        }
     }
     return 0;
  }

So there's a simple business rule in this case that a trip segment's distance cannot exceed the specified aircraft's range for the specified number of passengers.

So here you have it, in a nutshell, an overview of validation in JMatter, how to implement it in your applications, and how things work under the hood.

In the next tip, I will be discussing how to write your own editors and renderers for atomic types.

no comments

Tip of the Week: Value Objects

Posted by Eitan Suez Fri, 04 Aug 2006 06:28:00 GMT

Shedding Light on JMattter's Design. Part 1: Value Objects

The notion behind Value Objects is old. It actually dates back to a specific version of Smalltalk. I am not a Smalltalk programmer but I have read a little bit about Smalltalk. It was in that environment that the concept of MVC first arose. The book, The Art and Science of Smalltalk describes value objects.

In Java, everything has a getter and setter. The notion of the value object is that, instead of replacing the reference with another value, the reference stays constant, but its value changes.

So, instead of:

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

We do:

  final String name = new String("Some Initial Value");
  public String getName() { return name; }

And, to change the value of the name, we do:

  name.setValue("Some new Value");

I see two advantages to this. The first is that you avoid nullpointer exceptions if you erroneously try to manipulate a null property.

The second is much more important: if the reference stays constant, then when the time to build a user interface on top of my model, then I can setup views for all my properties, and each view (or renderer, or editor) can set itself up to listen to changes on its corresponding value. When the value changes, the model object fires a change event that allows the view to update itself.

When not using Value Objects, it's much more difficult, and more expensive to try to set up MVC: each time a value is set, the view must detach itself from the old value and attach itself anew to the old value. It's useless busy work. The alternative, of course, is for the view to attach itself as a listener on a property of the parent object. I picked the former. And it worked out nicely.

I can simply use the Value Object mechanism essentially to define by-value semantics on properties. This is how JMatter can distinguish a aggregation from an association, for example.

1 comment

Tip of the Week: Calendar DnD Support

Posted by Eitan Suez Tue, 18 Jul 2006 02:48:00 GMT

I put together a short movie illustrating the tip of the week. Follow the link.

no comments

IntelliJ IDEA Support

Posted by Eitan Suez Thu, 13 Jul 2006 20:54:00 GMT

Sam Griffith proposed on the jMatter mailing list (a couple of weeks ago) the construction of a jMatter application for the construction of jMatter applications.

I can imagine how one might simply instantiate a few instances of "Entity" in a GUI, specify field names, and other meta-information, and then layer behaviour on top of it, namely: "Generate Source Code."

This is a worthy goal. In fact, I clearly recall once seeing screenshots of an application built by Rick Mugridge for NakedObjects called "Emperor" (although as far as I know, this application was never made accessible to a wider audience).

We will get there. But in the meantime, I thought I'd post a few simple IntelliJ IDEA live templates for jMatter that I recently put together. This is nothing fancy, but it's a small, first step.

The xml file, jmatter.xml, can be dropped into the config/templates/ directory of your user configuration area. On linux, the full path is ~/.IntelliJIdea50/config/templates/

So, for example, if you've defined a class named Talk and a many to one association to a Speaker via a field named spk, instead of hand-coding the JavaBeans bound property's getter and setter methods, you can instead type:

    bprop[tab]

and fill in the two arguments: spk[tab] and Speaker[tab] and you're basically done.

I've defined a little over a dozen such simple live timeplates (fo for fieldOrder, cc for ColorCode, cmd for defining a command method, etc..

2 comments

Tip of the week: Associations Made Easy

Posted by Eitan Suez Fri, 07 Jul 2006 20:28:00 GMT

NakedObjects originally introduced a terrific way to establish associations between objects in a user interface. It automatically enabled this via drag and drop.

For some reason, dnd in many ways is not used as much as it used to be. Having to take the hand off the keyboard is admittedly a "productivity mark deduction."

So, in JMatter, I set out to enable associations in more than one way. For starters, dnd works out of the box. But one can also click a "pick" button that will display a listing of entries of the type for a given association, that the user simply picks from.

To make the example concrete, let's say we're working with the Symposium manager demo application in JMatter. And let's further say that we're defining a new talk to be given. Talks have a many-to-one association to the speaker (or presenter) giving the talk.

My favorite way of establishing that association is with the in-place associator widget that's built-in to JMatter. The way it works is that you simply start typing and the list of entries automatically filters out entries that do not match. It's that sort of dynamic JComboBox that web browsers popularized, I suppose. Except that the data comes straight from the database.

Here's a snapshot of me trying to define a new talk, In Action, as it were:

Eitan Associating a Speaker to a talk

(Please pardon the humour if you find it in ill taste :-) )

The only detail to tend to is to specify which field of Speaker are we searching by. That's what that little 'magnifying glass' icon to the left of the text field is for. It's a drop-down combobox that dynamically lists the various fields you might want to search by.

Here's how you control the default field that JMatter should use:

static
{
   ComplexType type = ComplexType.forClass(Speaker.class);
   type.setDefaultSearchField(type.field("name").field("first"));

}

And that's the tip of the week.

no comments

Article on clientjava.com

Posted by Eitan Suez Wed, 05 Jul 2006 00:20:00 GMT

I recently wrote an introductory jMatter article for clientjava.com. The article just went online yesterday.

no comments

Plugin Inversion

Posted by Eitan Suez Thu, 29 Jun 2006 14:48:00 GMT

Between writing the documentation for jMatter, doing the InfoQ interview last week, and writing a soon-to-be-published article, I've found myself many times asking the same question: how can I describe jMatter succinctly?

It's most interesting that when one looks once more at something he (or she) believes he already knows, one ends up discovering new things, or new ways of looking at the same thing. So here's a new way of thinking about jMatter (which is never entirely correct, but contains kernels of truth), which I stumbled upon and which I find to be somewhat delightful, somewhat humorous.

jMatter can be described as inverting the idea of a plugin. It's an application that defines a plugin API for a domain model. Different developers will write different domain models into jMatter and each will end up with a different application, where all conform to the same user interface conventions, and all have many similar or consistent features.

Thinking about this some more, we discover that there's a tremendous upside to this way of doing things: as users, we often complain about the cost of learning to become a power user, the cost of learning the user interface for a given software application, of discovering its behaviour, what to do, what not to do, etc... Each software application has its own user interface. With jMatter, all applications have the same, consistent user interface. Learn to become a power user with one, and you're instantly rewarded by the fact that you can use any jMatter application, irrespective of the domain. I think that's powerful stuff. And it promises to collectively save us thousands of man-hours.

3 comments

Interview with InfoQ!

Posted by Eitan Suez Sat, 24 Jun 2006 03:20:00 GMT

InfoQ just published an interview they did with me about jMatter. Check it out at:

http://www.infoq.com/news/jMatter

no comments

jMatter now available Open Source!

Posted by Eitan Suez Mon, 19 Jun 2006 00:52:00 GMT

I am proud to announce that the jMatter framework license terms have been revised. jMatter is now available either under the GPL or the jMatter commercial license.

no comments

Older posts: 1 2 3 4 5

Powered