HEAD PREVIOUS

Chapter 8
Dichotomy of a Model Object

Let's begin by taking a look at a very simple model for a Person:
package com.u2d.contactmgr;
import ...
@Entity
public class Person
      extends AbstractComplexEObject
{
   private final StringEO name = new StringEO();
   public Person() { }
   public StringEO getName() { return name; }
   @Cmd
   public String SayHello(CommandInfo info) { return "Hi"; }
   public Title title() { return name.title(); }
}
We see that:
  1. At the most basic level a class definition consists of data and behaviour, implemented as fields and commands (or methods), known as the type's members.
  2. JMatter relies heavily on conventions for the purpose of inferring information about a type.
Here are some of the conventions:
  1. Our class must extend the base type AbstractComplexEObject. This base class is a partial implementation of the ComplexEObject interface.
  2. We have a field, the person's name, defined via a private variable but inferred from a getter method
  3. We have a command, "Say Hello", implemented of course as a method.
  4. Every type must also provide an implementation of the method title(), which in a sense defines instances' labels, or captions in the application's user interface. The Title class exists simply to facilitate the task of constructing a title when we wish those titles to be constructed by concatenating various field values.
  5. We use the @Entity annotation to mark the type as one that we want to persist to database.
  6. We mark a method with the @Cmd annotation to indicate that we wish to expose this method to the user interface.
There exist a number of subtleties too. Here are some examples:
  1. For command methods that return a String, the returned string is implied to be a message returned by the action that JMatter will display to the user.
  2. Commands can accept arguments (I'll have more to say about that later).
  3. Type commands can be defined by marking the method as static.
  4. JMatter infers the type names and captions, member names and captions from our choice of class name, getter method name, and command method name. This is done via run-time reflection in Java.
The total sum of conventions and features supported by JMatter is much lengthier than this basic introduction. Let us then delve into the details.

8.1  Types

When building a JMatter application, your job as a developer is to develop the model completely, using object-oriented techniques: accounting for both data and behaviour.
You can think of the complete application as having a model-view-controller design. In the case of JMatter, the view and controller are generic, and already implemented. This frees you to focus on the model.
From a persistence perspective, all types inherit three fields or pseudo fields:
Field Description
id Serves as surrogate primary key. You do not need to define a primary key for your types. The framework does this for you.
version This field is used by the underlying hibernate persistence framework to keep track of whether two objects are being edited at the same time, used for optimistic locking.
createdOn A read-only field that records the date and time that an object is created. When displaying a view of an object in form view, the value of this field is displayed at the bottom, in the view's status bar.

8.2  Fields

JMatter makes a distinction between atomic fields and complex fields, where atomic fields have a single value, while complex fields are in a sense composed of atomic and other fields.
There's a second distinction that has to do with ownership of a field. Composite, or aggregate fields are ones that are wholly owned by their parent object. All atomic fields are aggregates. Associations on the other hand are fields that represent relationships to other objects, or entities; these relationships can be set or severed. Field ownership has implications on specific operations. For example, if the parent object is deleted, associations are only severed, but the associated object is not deleted. That is, the delete operation does not cascade to associations. A deletion on the other hand implies the deletion of child aggregate fields.
It turns out that JMatter's domain design happens to match the rules set forth by Eric Evans in his book Domain Driven Design [5]. If you're familiar with this work, then the notions of entities, value objects, and aggregates and their interrelationships should already by familiar to you.
We also need to discuss how to model fields with different cardinalities. There are of course to-one relationships, and * or to-many relationships.

8.2.1  Atomic Fields

JMatter defines and provides implementations for a variety of types of atomic fields. Developers are required to use the framework's definitions. That is, a string-based field must be defined as a com.u2d.type.atomic.StringEO and not a java.lang.String. Internally the frameworks' atomic type implementations often wrap or encapsulate the more primitive type.
The framework's atomic types are modeled as value objects. JMatter defines an interface for Atomics, com.u2d.model.AtomicEObject.
The framework requires that all aggregate type fields be implemented as final fields. Consequently accessor methods for these fields comprise of only a getter method. The definitions look like this:
private final StringEO name = new StringEO();
public StringEO getName() { return name; }
You may wonder then how one sets the value of an object's name field? All value objects have a method:
public void setValue(EObject value);
to provide a means for setting the value. That is, the reference to the object never changes, only its contained value. StringEO has an additional convenience method:
public void setValue(String value);
Other atomic types have similar conveniences. IntEO for example (which represents an integer) has this convenience method:
public void setValue(int value);
These types also have a corresponding way of getting the represented value as a simple Java primitive or object. IntEO has:
public int intValue();
StringEO has:
public String stringValue();
..and, you guessed it, DateEO has:
public Date dateValue();
Finally, all these atomic types are ChangeNotifier's, which means that they publish (or fire, in pub/sub speak) change events that you can listen to. This can be useful for building lotus123-style dependencies between objects (when A changes, update B). This is also useful internally to keep the user interface in sync with its underlying model.

8.2.1.1  Build-In Atomics

Here is a listing of the various JMatter atomic types:
BooleanEO, CharEO, ChoiceEO, ColorEO, DateEO, DateTime, DateWithAge, 
Email, FileEO, FileWEO, FloatEO, ImgEO, IntEO, Logo, LongEO, 
Password, Percent, Photo, SSN, StringEO, TextEO, TimeEO, 
TimeInterval, TimeSpan, URI, USDollar, USPhone, USZipCode.
They are all defined in the package com.u2d.type.atom. Writing your own additional types is quite straightforward. The existing implementations represent a wealth of examples that one can draw from.

8.2.2  Composite Fields

There isn't much to say about composite (or aggregates) fields that I haven't already. Again, what makes a field an aggregate in the framework is the fact that its member is defined as a value object: define a final field, and only a getter method. Here's an example:
public class Person extends AbstractComplexEObject implements Emailable
{
   protected final Name _name = new Name();
   public Name getName() { return _name; }
   ...
}
The setValue() method applies to composite fields. For example, to copy one contact's address to another by value, you might do this:
public void copyAddress(Contact fromContact, Contact toContact)
{
  toContact.getAddress().setValue(fromContact.getAddress());
}

8.2.2.1  Composite Types Provided with the Framework

The framework predefines a number non-atomic, or higher-level types. They're reside in the package com.u2d.type.composite. Here's a list of type names:
Name, USAddress, ContactMethod, Contact, BusinessContact, 
EmailMessage, Folder, Note, Person, Business, LoggedEvent

8.2.2.2  Composite Indexed Field

JMatter provides a means for modeling a composite field with a to-many (*) cardinality. A prototypical example is the relationship between an Order and its list of OrderItems.
JMatter provides the type com.u2d.list.CompositeList for the task. Here's an example field definition inside an Order class:
private final CompositeList _orderItems = 
                 new CompositeList(OrderItem.class, this, "order");
public static final Class lineItemsType = OrderItem.class;
public CompositeList getLineItems() { return _orderItems; }
Here's the constructor method signature:
public CompositeList(Class clazz, ComplexEObject parent, String parentFldname);
The first argument is the child type (Order Items). The second is a reference to the parent object (this). Finally, there's a convention where CompositeList can automatically give child objects a reference to its containing, parent object. This is done by writing a setter method in the child type definition:
private Order _order;
public void setOrder(Order order) { _order = order; }
and passing the name of the field (order) as an argument to the CompositeList constructor.
Notice that you also must (at the present time) provide metadata for the framework to introspect the list type statically. The framework looks for a public static variable name of type Class of the form <fieldname>Type.
In the user interface, JMatter provides an tabular component that provides means for adding child items, deleting them, editing them, etc..

8.2.3  Associations

8.2.3.1  To-One Associations

A to-one association definition is essentially the implementation of a JavaBeans bound property. Let's look at an example, Song.artist:
private Artist _artist = null;
public Artist getArtist() { return _artist; }
public void setArtist(Artist artist)
{
  Artist oldValue = _artist;
  _artist = artist;
  firePropertyChange("artist", oldValue, _artist);
}
That's about it.

8.2.3.2  To-Many Associations

For an indexed association, use the type com.u2d.list.RelationalList. Here's an example, from the Sympster demo application (Speaker.talks):
public class Speaker extends AbstractComplexEObject
{
  ...
  private final RelationalList talks = new RelationalList(Talk.class);
  public static final Class talksType = Talk.class;
  public RelationalList getTalks() { return talks; }
  ..
}
We do three things:
  1. define a final member variable, talks
  2. provide metadata for the framework to statically introspect the type to determine the list type
  3. supply a getter method

8.2.3.3  Bidirectional Associations

We often like to be able to navigate an association from both directions. Take the example of a speaker and his or her list of presentations. The presentation would have an association to its speaker, and the speaker may have a to-many association called presentations.
You can define both associations in the manner described in the two previous subsections:
public class Speaker..
{
  ...
  private final RelationalList talks = new RelationalList(Talk.class);
  public static final Class talksType = Talk.class;
  public RelationalList getTalks() { return talks; }
  ..
}
public class Talk..
{
  ...
  private Speaker speaker;
  public Speaker getSpeaker() { return speaker; }
  public void setSpeaker(Speaker spk)
  {
    Speaker oldValue = this.speaker;
    this.speaker = spk;
    firePropertyChange("speaker", oldValue, this.speaker);
  }
}
In addition, you need to supply metadata for the framework to infer the other side of the relationship. The implementation at the moment is not too elegant but is nevertheless simple enough. In class Talk, add:
public static String speakerInverseFieldName = "talks";
And in Speaker:
public static String talksInverseFieldName = "speaker";
Basically, the field name is of the form: <fieldname>InverseFieldName. Speaker has a field named talks, so the variable name is talksInverseFieldName. Its value is the name of the inverse field: the talk's speaker's field name is speaker. Both sides must define the bit of metadata.

8.2.4  Choice Types

The notion of a Choice in JMatter is somewhat analogous to what programmers call enumerations. JMatter provides two flavors of choices. The first and more basic flavor is the ChoiceEO and is implemented as an atomic object.
This flavor is suitable for enumerations (or small lists) that are static; i.e. they do not change. The framework itself implements a couple of types as ChoiceEO's. As part of my work on providing access control to information, I define a FieldRestrictionType (in package com.u2d.restrict) as follows:
public class FieldRestrictionType extends ChoiceEO
{
   public FieldRestrictionType() {}
   public FieldRestrictionType(String value)
   {
      setValue(value);
   }
   
   private static Set STATUS_OPTIONS = new HashSet();
   static
   {
      STATUS_OPTIONS.add(FieldRestriction.NONE);
      STATUS_OPTIONS.add(FieldRestriction.READ_ONLY);
      STATUS_OPTIONS.add(FieldRestriction.HIDDEN);
   }
   public Collection entries() { return STATUS_OPTIONS; }
}
We also wrote a ChoiceEO in chapter 7 to define the various states for an Issue. Basically the way to define such enumerations is by extending the base type ChoiceEO and implementing the method entries(). The editors for these types are Swing JComboBoxes (pick lists).
The second flavor is modeled as a ComplexEObject. The data is not hard-coded and the type is defined as an entity in the database. These lists are more versatile in that one can define new entries, new choices as a system evolves. We developed an example implementation in the MyTunes tutorial chapter, to define song genres.
The framework provides full create/edit/delete capabilities for these types. They are defined by extending the type AbstractChoiceEO. The framework itself defines four such types of choices: US States, Sex, Marrital Status, Contact Method (instances of these four types are pre-populated from the xml files located in jmatter/resources/data).
Both flavors adhere to the Choice interface:
public interface Choice
{
   public String code();
   public String caption();
}

8.3  Commands

Let's discuss how commands work in detail. Here is the basic syntax:
@Cmd
public <modifier> <returntype> <MethodName>(CommandInfo cmdInfo, 
                                 [additional arguments]) { <body> }
This is not really that different from the task of defining any method. The main differences are:
  1. Marking the method with the @Cmd annotation exposes it to the UI
  2. The first argument to the method is some context information about the command invocation (at the moment you don't really need to concern yourself with the CommandInfo type; I am discovering that this information is rarely needed and am beginning to question passing this argument).
Making the method static has the effect of defining the command on the type instead of its instances. You can see the command action exposed as a context menu on the type in the classbar (or anywhere else, say after browsing types).
If the method returns a String, that string is treated as a message that is displayed to the user in a sort of self-dismissing dialog box.
If the method returns a type that is viewable (another entity), the framework makes sure to display a view of the returned instance after the command is invoked.
If the method accepts arguments, then the framework will first prompt the user for these arguments (via an input form) before proceeding to invoke the command. Take for example, this command, defined on User.java in the framework:
@Cmd(mnemonic='p')
public String ChangePassword(CommandInfo cmdInfo,
                              @Arg("New Password") Password password)
{
   _password.setValue(password);
   save();
   log(LoggedEvent.INFO, cmdInfo.getCommand(), "User changed password");
   return "Password has been changed";
}
When an end-user invokes this command (by pressing the Change Password button on an instance of a user), he or she will first be prompted to enter a password, using the caption New Password. The framework does it all. Once the password is entered, the framework proceeds to invoke the command (in the proper thread). In this case, the password editor takes care to properly validate the password before the method is ever invoked.

8.3.1  Types of Commands

So we see that with the @Cmd annotation, we can expose a method to the user interface. Also note the subtlety of adding the static modifier to define a command on a different object: the instance's type, rather than the instance itself.
So we have two types of commands: instance commands and type commands. In the JMatter framework, the Java class ComplexType represents or models a type, in the same way that the class java.lang.Class is an instance's type. You will also find @Cmd definitions in ComplexType. So the JMatter framework uses many of the concepts that it defines for application development internally, to build itself. Also this should make you realize that a type command on a given object is nothing more than an instance command on its type instance.
Atomic types can have their own commands too. They can be invoked from the UI by right-clicking on the rendered value. For example, StringEO has the command Capitalize(). We encourage you to define additional commands that might be useful to the plethora of atomic types that JMatter defines.

8.3.1.1  Type Commands

Aside from marking a method static to designate a command belong to the instance's type, you also have the option of specifying a method signature with an extra parameter, like this:
@Cmd
public static void SayHi(CommandInfo cmdInfo, ComplexType targetType) ...
The last parameter is optional. If specified, then JMatter will make sure and pass a reference to the target object (a ComplexType) upon which the command was invoked. Even though the method is static, it was invoked in the context of an instance, which happens to be a type. In some circumstances, the called method needs to know what context it was called in, and needs a reference to that object.

8.3.1.2  List Commands

Aside from type and instance commands, there is a third type of command: list commands. List commands can be defined in several ways.
First, if an instance command is given the annotation attribute batchable as shown here:
@Cmd(batchable=true)
public void Email(CommandInfo cmdInfo) ...
This is an indication that the command can be invoked in batch, so to speak; that is, given a list of instances (perhaps search results from a query), you will be able to invoke that command on all the items in the list. The logic basically iterates over all instances in the list and invokes the command on each instance.
There exists a command Email on JMatter's type Person, which launches your default email client and opens a compose window with the destination address pre-filled with the person's email address information. You can try this from the ContactMgr demo application. To invoke it in batch mode, just bring up a list of contacts and right-click on the list-view's title to reveal its context menu.
Invoking the Email command in batch will open multiple compose windows, one for each recipient on the list.
The additional command: EmailWithSubject, defined like this:
@Cmd(batchable=true)
public void EmailWithSubject(CommandInfo cmdInfo, @Arg("Subject") StringEO subject)
prompts you for a subject line and then launches your email client's compose command. Note that this command is also marked as batchable. JMatter is smart enough to ask you for the subject line once, irrespective of the number of times the command is invoked.
What if we wanted the behavior of the command to be smarter? That is, we wish to write a separate implementation of Email, perhaps named EmailAll, that opened a single composition window, but that specified multiple addresses (the addresses of each of the persons on the list) in the To: field.
To do that, define a list command, like this:
@ListCmd
public static String EmailAll(CommandInfo cmdInfo, AbstractListEO list) ...
The command will be exposed on the list and the method will be invoked, passing in the list reference as an argument to the method. You can now iterate over the list items to grab all the email addresses, but implement the method such that a single email message is composed.
Finally, List Commands also allow multiple parameters, here's an example:
@ListCmd
public static String EmailAllWithSubject(CommandInfo cmdInfo, AbstractListEO list, 
                                         @Arg("Subject") StringEO subject)
Note that list commands must be denoted as static. Please refer to the Person class for the complete code listing.
List commands are a powerful tool that can significantly increase the usability of applications. Their use is highly encouraged. Without them, end users may be forced to perform the same operation repetitively to effect a business change in their model.

8.4  Metadata

8.4.1  Overriding Plural Name

JMatter applications are all about managing information. Information stored in objects and presented in the user interface as objects. Every model object of course has a distinct name (or label) and icon, and a distinct set of fields and commands. The user interface displays a classbar, which in a sense is a starting point for performing various operations, listing information of a given type, searching for information, creating a new instance, etc..
The classbar displays the various types in iconized form: with a caption and an icon. Each type's caption is derived from its class name. It's actually the plural form of the class name. For a Boat, the plural form is simply Boats. For a Person object you might wish the caption to say People.
To override the default behaviour for deriving the plural for a type, define a static method named pluralName.
Example:
 
public static String pluralName() { return "People"; }

8.4.2  Overriding Natural Name

The name of a type as it is displayed in the JMatter ui is derived from the class name. There may be circumstances where you might want to override the natural name for a type. This is done in a manner similar to the above, by defining a static method named naturalName().

8.4.3  Color Coding

Types may optionally be color-coded. List views' and instance views' titles are painted using a gradient background color. By defining this field, for example:
public static Color colorCode = new Color(0x4169aa);
The framework will use the specified color as the starting color for the gradient. Additionally, list entries are painted such that row background colors alternate from white to another color. The type's color code, if specified, is used for that other color. Both these styling mechanisms make it easier to distinguish types in the user interface.
Color coding is generally a very effective tool for improving a user interface.

8.4.4  Icons

You can assign an icon to each type. Rather than edit a configuration file, the name of the icon is derived from the type name. JMatter supports .png, .gif, and .jpg formats. Two icons sizes must be supplied: a 16x16 pixel icon (used in listings), and a larger 32x32 pixel icon. The files must be placed in the resources/images folder of your project. Your project's build file automaticaly copies these resources to the classpath at images/<imgresourcename>.
For the User class, the files should be named User16.png and User32.png respectively (the file suffix may vary of course).
List views of a uniform type are also decorated with an icon. You can distinguish icons used for lists of a given type from icons used for individual instances: drop in Users16.png and Users32.png into the images folder. Note how the file name uses the pluralized form of the type.
JMatter also leverages and honors the type's inheritance hierarchy. For example, if you define a specific subclass of User.java, say UberUser.java, without providing a set of icons for the subtype, JMatter will fall back the super type's icon.
For types that are stateful, you can also distinguish between instances of different states by icon. For example, the JMatter framework provides a UserLocked32.png. If for some reason a user is locked, an administrator viewing a list of users can easily spot the locked user. The IssueManager demo application also leverages this feature (see chapter 7).

8.4.4.1  Per-Instance Icons

There are situations where one of the fields of a model object contains a picture representing the instance in question. Here are examples:
  1. An album's cover
  2. A person's photo
  3. A photo of a car part
So far, all instances of a given type use the same icon. Why not use a different icon per instance in such situations?
The methods iconLg() and iconSm() which are instance methods on model objects, govern what icon is to be used for an instance. Simply override the implementations in your classes. JMatter provides a utility class: PhotoIconAssistant, that takes the work out of properly scaling the photo to the right size for an icon, on your behalf. Here's an example of how this is done:
public class Speaker extends AbstractComplexEObject
{
  ...
  public ImgEO getPhoto() { ... }
  private transient PhotoIconAssistant assistant = 
                        new PhotoIconAssistant(this, photo);
  public Icon iconLg() { return assistant.iconLg(); }
  public Icon iconSm() { return assistant.iconSm(); }
  ...
}

8.4.5  Field Order

To control the order in which a type's fields are laid out on a form, use the fieldOrder static member.
Example:
 
public static String[] fieldOrder = {"name", "contact"};
Whatever field names you don't reference in fieldOrder will still appear on the form, after the listed ones, in no guaranteed order.
This piece of metadata underscores an important aspect of the framework which you must understand: the notion that fields and commands can be referenced by name, as a string. What are the rules that govern the derivation of a field's name?
Field names are derived from their accessor method name. Here are a couple of simple examples:
getName() -> "name"
getFirstName() -> "firstName"
Fields also have natural names, presented as field captions in the user interface. The natural name for the firstName field is First Name.

8.4.6  Command Order

In a manner analogous to the way fields are ordered on a form, we can control the order in which a type's commands are displayed with the commandOrder static field.
The ContactMgr demo application's PersonContact type has two static methods Report and NewPersonWizard. Their relative order can be specified as follows:
public static String[] commandOrder = {"Report", "NewPersonWizard"};
What's interesting here is that these two commands are static methods: they're type commands. The commandOrder field controls both static commands and instance commands. Each list is mutually exclusive and so can be (and is) specified with a single commandOrder string array field.

8.4.7  Flatten Into Parent

Types can have different types of member fields. Some are simple, atomic, fields such as a string, a number, a percentage perhaps. These fields are not entities. From a persistence perspective, they don't get their own tables. A type may also bear members which are not atomic, but nevertheless aggregates of their containing types. Say for example that I define a Person type with the field name of type is Name. The type Name in turn has two fields: first, and last, both of which are atomic fields. Person.name on the other hand is not atomic. The hierarchical relationship is sometimes important and useful in many situations.
By default JMatter maintains a one-to-one correspondence from model to view, and does not hide this relationship. The view widget used by JMatter's Swing-based view mechanism can expand or collapse to show the name field's children. An instance is shown in expanded mode below.
figures/person_name.png
Figure 8.1: Hierarchical Display of Aggregates
Sometimes it's more practical to display all descendant fields as a single flattened form, rather than require users to expand and collapse sub-nodes in order to perform data entry. That's what the flattenIntoParent metadata bit is for. In our example, to communicate to the framework that we wish to flatten the name field's child fields (first and last) into its parent form, we would do this:
public static String[] flattenIntoParent = {"name"};

8.4.8  Tab Views

Another feature related to controlling an aspect of how information is displayed to users is the tabViews metabit. You use it in a manner similar to many other pieces of metadata: by specifying a list of member fields:
public static String[] tabViews = {"contact"};
The above example may be used, say, in a Person object to specify that the person's contact information (an aggregate field) should be displayed in a separate tab.
In the Swing-based user interface, the keyboard shortcut Alt-<index> can be used to switch the focus to the tab at index <index>. That is, in a view with three tabs, the shortcuts Alt-1, Alt-2, and Alt-3 can be used to toggle tab focus.

8.4.9  Default Search Path

This piece of metadata is useful to define one field that is most commonly used to search for instances of a given type. It may be name for a Person object, or title for a Presentation object. Here is how to specify it:
public static String defaultSearchPath = "name";
The value is specified as a string but may actually be a field path, such as name.first.
This value in turn is used in the user interface to assist with quick searches for performing associations or defaulting the search field when performing a search operation. It's quite useful. At the moment, this feature is supported only for text-based fields where the inequality defaults to TextStarts (i.e. that we're looking for instances whose default search field value starts with the value entered by the user).

8.4.10  Unique Fields

JMatter automatically takes care to define a surrogate primary key for your model classes. Often there may exist additional fields, natural keys, that must remain unique. Optionally defining a static field "identities" will ensure that JMatter constructs the database schema properly (marking the field as unique in the database, which will also create an index on the table). In the user interface, such fields cannot be revised after an object has been created for the first time.
Example:
 
public static String[] identities = {"ssn"};

8.4.11  Read-only Fields

Fields can be marked as "read only" by providing an optional "readOnly" static field (in the same manner fieldOrder is specified). This is useful when working with derived (or calculated) fields, which we discuss next.
Example:
 
public static String[] readOnly = {"created"};

8.4.12  Calculated Fields

We sometimes use fields whose values are derived from other information. The information does not need to be persisted (made part of the database schema) and the information can simply be derived when the object is restored from the database.
Define such fields in the usual way. JMatter provides a way to mark the field as "not persisted", and a way to hook into the object's life cycle in order to update the field value, as illustrated in the following example.
Example:
 
private final USDollar _total = new USDollar();
@Fld(persist = false)
public USDollar getTotal() { return _total; }
public void onLoad()
{
   super.onLoad();
   calculate();
}
private void calculate()
{
   ChargeCalculator calc = new ChargeCalculator(_trip, _settings);
   _charges = calc.getCharges();
   _total.setValue(calc.getTotal());
}
Since derived fields need to be recalculated as a function of their inputs, it's also useful to attach change listeners on the input values. Here's an example:
_settings.addChangeListener(new ChangeListener()
{
   public void stateChanged(ChangeEvent e)
   {
      _tracer.info("Settings changed, recalculating..");
      calculate();
   }
});
In this case the variable _settings is a composite member of the enclosing class.

8.5  Per-Field Metadata

8.5.1  @Fld Annotation

We can augment a field's getter method with the @Fld annotation. Here is a list of the parameters this annotation accepts, along with a description (all of these parameters are optional).
Parameter Name Type Description
mnemonic char What keyboard mnemonic to attach to the field in question. Useful for form entry, to directly set the focus on a specific field
label String A way to override the caption used to display a field in a form
description String The field's description. Used as the contents of a tooltip on the field.
colname String A means to override the name of the database column corresponding to this field. By default, the column name is automatically derived from the field name.
colsize int Controls the size of the field's underlying database column. By default, many string-type fields are set as varchar(255). This attribute provides a means to control the column size at a finer level.
displaysize int Controls the size of the corresponding text field used for doing data entry on the field.
format String Applies only to TimeEO and DateEO type fields. Accepts a SimpleDateFormat string to control the format of times and dates for both parsing and rendering. e.g. @Fld(format='m:ss') might be useful on a TimeEO field for displaying song durations.
persist boolean Whether the field should be persisted at all (see the discussion on Calculated Fields)
hidden boolean Use to hide certain fields from the user interface
Although the framework provides a means for overriding the label for a field, we generally discourage this practice. We believe there is great value in maintaining a correspondence between field labels in the user interface and their corresponding field names in our object models.
If you wish to revise the date and time format throughout the application (i.e. not for a specific field), you can specify the format in the model-metadata.properties file (see subsection 8.6.3).

8.5.2  The @IdxFld Annotation

For relational lists, one can optionally specify an additional annotation: @IdxFld. At the moment, you can specify two attributes:
ordered=true
Will maintain the list as an ordered list. From a persistence perspective, the hibernate mapping file will use an ordered list (an additional column in the database table specified the list item order). From a UI perspective, in edit mode, the list can be reordered via drag and drop.
ownsChildren=true
If specified, the to-many relationship is modeled in such a way that when an item is removed from the list, the child item will be deleted from the database (hibernate cascade=delete-orphan). Semantically the list items cannot be freely associated with an entity. In the user interface, the "Browse" and "Find" association commands that usually adorn the list view are not accessible.

8.6  Per-Command Metadata

Commands must be marked with the @Cmd in order to be exposed to the user interface. There's nothing special about these methods and they can be invoked programmatically as well. When these commands are invoked from the user interface, JMatter makes sure to call the method off the event dispatch thread.
If a command returns a ComplexEObject, its view is drawn and displayed back on the event dispatch thread. The framework also makes sure to display a waiting cursor while the operation is invoked.

8.6.1  @Cmd Metatada

Here is a summary of the optional parameters that the @Cmd annotation supports:
Parameter Name Type Description
mnemonic char What keyboard mnemonic to use to invoke the command
label String Use to override command's caption in the user interface
description String Used as a tooltip to the command's corresponding view (usually a button)
sensitive boolean Specify whether this is a sensitive command (e.g. Delete) where it would be beneficial for the ui to provide a mechanism to prevent against inadvertent invocation of the command. This is done in the Swing view mechanism by disabling the command's button. A small lock on the button can be clicked on to unlock (enable) the action.
shortcut String Applies only to type commands (commands marked static). Specify a keyboard shortcut (an accelerator). Example: "control Z"
batchable boolean Whether the command can be invoked in list context (composed across a number of instances)
iconref String Decorate the command view (button or menu item) with an icon. For example, the Edit command is defined with iconref="pencil"; make sure to place a copy of pencil32.png and pencil16.png in the image resources folder.
Note: although the framework provides ways of overriding field and command labels, we highly discourage this practice. Having a one-to-one correspondence between the action's label and its corresponding method name significantly improves the maintainability of your application.

8.6.2  @Arg Metadata

In addition, method arguments can be optionally marked with the @Arg annotation. This annotation accepts a single value, which is used as the caption on the form drawn to accept input to the method, as this example illustrates:
@Cmd(mnemonic='p')
public String ChangePassword(CommandInfo cmdInfo,
                              @Arg("New Password") Password password);

8.6.3  The model-metadata.properties file

By default all child projects have a file that can be customized to provide model metadata: resources/model-metadata.properties. Here are the default contents of this file:
User.role.required=true
User.role.default=from Role r where r.name='Default'
CompositeQuery.name.required=true
# e.g.
#Speaker.name.required=true
# date and time format override (optional)
#DateEO.format=dd.MM.yyyy
#TimeEO.format=HH:mm:ss
Although this file is discussed in the next chapter from the perspetive of validation, not all aspects of this file pertain to validation.
First, we see that we can use a sort of field path notation to reference a field, such as User.role, or perhaps Person.name.first.
We can mark fields as required by setting the field property value, appending the .required suffix, to true. We can also specify a field's default value. Above we see that we can even specify the default value in terms of a hibernate (hql) query. We can also provide a hard-coded value, say Person.salutation.default=Mr.
For choice-type fields, we can specify the code for the choice and if a database lookup is necessary, it will be performed (e.g. Address.state.default=TX)
Finally, we just recently inroduced the ability to specify the default date and time format, which previously was hard-coded to US norms. The format is specified using Java's SimpleDateFormat rules.

8.7  Persistence Lifecycle and AppEvents

The base class AbstractComplexEObject implements the interface PersistorListener:
public interface PersistorListener
{
  public void onLoad();
  public void onBeforeCreate();
  public void onCreate();
  public void onBeforeSave();
  public void onSave();
}
The persistence mechanism implementation calls these methods at the appropriate times: when an object is loaded from the persistence store, onLoad() will automatically be called, for example.
It is permissible to override these methods, but be sure to first call the superclass's implementation. That is, do not mask the superclass implementations:
public class Car extends AbstractComplexEObject
{
  public void onLoad()
  {
    super.onLoad();
    System.out.println("Loaded "+this);
  }
  ..
}
This is useful for some things. For example, onLoad() is useful for recalculating a derived field. The framework itself uses onBeforeCreate() to set the value of the read-only field createdOn:
1   public void onBeforeCreate()
2   {
3      _createdOn.setValue(new Date());
4      fireAppEventNotification(BEFORECREATE, this);
5   }
This bring up another aspect of the framework that might be useful to know about: AppEvent's.
The JMatter framework contains an implementation of the Observer pattern (aka publish/subscribe) that is used internally for communicating various events between various parts of the application. For example, the framework defines LOGIN and LOGOUT events. When a user logs in, this event is published and all interested listeners are notified. The framework's class AppSession is an AppEventNotifier in that it notifies listeners of login-related events.
AbstractComplexEObjects do a similar thing. By virtue of being persistor listeners, they are notified of persistence lifecycle changes. These objects use the AppEvent mechanism to also notify any interested listeners of these events. That explains line 4 in the above code sample.
To the extent that you have objects in your application that need to be informed of such events, these objects may configure themselves as listeners on whatever model objects they're interested in.
In fact, we saw an example in the IssueMgr sample application where we wanted to delay transitioning to another state until after the object was created.

HEAD NEXT