5.4.4 Changes to Support MIDP
Extending the Watch example to support MIDP required
surprisingly few changes. In fact, more changes were made because of minor
problems noticed in the initial code than because of MIDP.
One such problem was an area that we decided not to
support initially, because of the lack of use: how should an alarm react when
the clock time is changed. We had built metamodel support for this (alarms have
a property to say whether they are dependent on local time: true for an alarm
clock, false for a countdown timer), but had left out the implementation in the
framework classes as being not worth the effort. “After all, who is likely
to lug a PC around to use our watch application’s alarm?” we
thought. How short-sighted we were!
Metamodels
No changes were required to the metamodels to support MIDP. In
the course of improving the support for alarms in the context of a user changing
the time on the watch, it was noticed that the existing metamodel allowed Roll
roles to VariableRef objects: i.e. changing the value of a function result or
variable from somewhere else. This was corrected with a constraint that Roll
could only apply to a Variable (local to this graph), not a
VariableRef.
An addition was made to the top-level graph for the
included components (framework classes), to allow different framework classes
for different ‘Generation target
platforms’.
Models
No changes were required to the models to support MIDP. To
support the improved alarm handling, we found we had to buffer the clockOffset
VariableRef in the Time application, editing the buffered copy and then setting
it back into clockOffset when the user exits the edit states. This improves the
behavior too: otherwise all alarms would be updated on each Roll up or down of a
digit, causing alarms to ring during editing.
Generators
We had made three different implementations of a Java state
machine whilst making the original watch, with ideas sketched out for another
two. The implementation we went with required the reflection abilities of Java,
which unfortunately are not present in MIDP. Hence we moved to a switch case
based implementation, using initialized static final variables as labels.
This required an addition to the _Variables generator to
generate the new static final variable for each Action and DisplayFn. Similarly,
a minor change was made to the _TransitionData generator to generate the
variable names rather than a string containing the same text.
The _Actions and _DisplayFn generators were similarly
changed to place their body inside a case statement, rather than a similarly
named function.
These changes were all largely cosmetic, and only the
generation of the initialization values for the static final variables required
a little ingenuity, as Java limits these to being integer expressions which do
not refer to any other variable – not even another static final. The
addition of variables to the generator definition language has since made other,
easier, approaches possible.
A larger amount of work was required for the new
‘_create make for MIDP’ generator. MIDP compiles its Java as for
other platforms, but it also requires a pre-verify step, and a couple of
configuration files naming and providing information about the MIDP suite (Watch
family) and the MIDP applications (Watch models) it contains. Normally these
configuration files would be written by hand, or filled in to a form, but in
principle all the information needed can be obtained from the code (or in this
case, the models).
The overly-tight constraints on these configuration
files’ formats made generating them with only the MetaEdit+ 3.0 reporting
language and DOS batch commands something of a challenge (more recent MetaEdit+
versions make this easy). Having to support several Windows versions, each with
different DOS commands and behavior, hardly made the task any easier. In the end
it was accomplished with a couple of helper batch files, which generate
sequential numbers and report the size of a specified
file.
Framework code
The original code was much in need of refactoring, having been
the authors’ first Java application, and not really intended to be looked
at. First we refactored out the mass of user-interface, control and state
machine behavior from the applet into the classes of their own. From this, it
was easier to see what had to be done.
The majority of classes were platform independent,
requiring only basic Java functionality. The user interface and control APIs are
different for MIDP, so a separate WatchCanvas class had to be made for MIDP.
Being a second attempt at the same functionality, with more Java experience than
before, it was soon noticed that the same solutions could be applied to the
WatchCanvas class for applets too. This resulted in smoother updating in the
applet, as well as keeping the applet and MIDP versions more visually similar.
As a result, some behavior is still duplicated between the two platforms’
versions of that class, but not enough to merit refactoring it out into its own
class.
MIDP does not have the Applet class, so our Applet was
replaced with a Midlet, the MIDP equivalent. There appears to be no reason why
an Applet should not have been used for MIDP: the same functions are present,
just with different names. As our generated applet classes subclass from
AbstractWatchApplet, our framework subclass of Applet, they work as subclasses
of AbstractWatchApplet just fine, even when it is a subclass of Midlet. Thus, no
changes were necessary to the generator that generates the applet/midlet for
each WatchApplication.