Home >> Media Group >> Research >> ViPER
Downloads -- Documentation -- Developers -- Discussion
LAMP     The Language and Media Processing Laboratory

Limn3 Design Specification

Introduction

Limn3 (nee AppLoader) was initially designed to support loading java objects and initializing them. It is built around this central idea, although it does have more than a few classes that do other things that might be useful.

The system itself is based around an RDF triple store, hosted through Jena, that keeps track of all the loaded beans, menu items, localization strings, and anything else that may be useful to loaded components. The triple store has event handlers, unlike the current Jena system. A more accurate description may be 'persistant queries', although they don't yet offer the complete set of features provided by RDQL. This allows the system to communicate through the RDF model, and lets the application developer, and advanced user, edit the application itself with a text editor.

System Management

This section focuses on the abstract nature of how the system loads components and how it responds to changes in the preference model. As stated above, the core of the system is an RDF triplestore and a core bean, which provides the menu panel and a place to attach other GUI elements. Modifying the triplestore will throw events to available listeners, acting as a high level, but slow and annoying to program, information bus.

Bootstrapping

The system works through the AppLoader bean, which requires a PrefsManager to initialize the system. For this, it loads a 'system model', the inital set of triples. The preference manager is then passed to an OptionsManager, which handles command line arguments - converting them to triples when necessary, or running various commands specified in the preference model. The AppLoader bean is then constructed with the PrefsManager as a parameter. The AppLoader is a subtype of JFrame, and includes a place to add other components and menu items. In its constructor, it loads and initializes the application beans, sets up the menu, and initalizes itself as a java window application.

The first step is loading the System model. The system model is either loaded from the file found at the "lal.prefs" property (passed to the jvm with -Dlal.prefs="file name"), or from the file found at the location "CONFIG/system.n3" or "CONFIG/system.xrdf" (useful for packaging jar applications, which don't seem to allow arguments to the vm).

After the system model is loaded, the options are activated. The options are specified as `lal:PreferenceFlag`_ or lal:PreferenceTrigger resources. These present a unified method for defining the command line options, preferences and system variable handling, as well as automatically generating the usage statement (along with the lal:Core lal:shellCommand "appname <options>" . triple). Triggers represent key/value pairs, and flags represent toggles. A preference object can have a short name (a single character abbreviation proceded by a single dash on the CLI, specified by lal:abbr , a standard name that is proceeded by two dashes, specified by an `rdfs:label`_ , and a long name, which is used for java system properties, which are passed via the -D option to the java virtual machine, specified with the lal:longName property.

After the preference triggers and flags are parsed, the OptionsManager puts them into action. First, it sorts through the system properties for the longNames. Then it parses the command line in order. For each trigger or flag parsed, the system invokes the functor specified specified with the lal:invokes property. The functor class must implement the TriggerHandler interface. The current system doesn't implement concatenating short names, like tar does, but there is nothing really preventing this, and it might be useful for some applications (find, tar, and other cli-oriented apps are possiblities).

There are two implementations of TriggerHandler included with the AppLoader. These are PrintUsage and PropsToPrefs. PrintUsage does exactly that, printing out a usage statement to the command line and exiting without an error code. The PropsToPrefs handler converts the value of the preference trigger into a triple, inserting the reified value of the lal:inserts into the system model; if it is a flag, it inserts it unmodified, but a trigger will add any missing objects in the inserted triples as a string literal.

With the model complete, and assuming no options have resulted in an exit or other change in the flow of the program, the Core bean is loaded. The Core then loads and initialized the other application beans. Note that beans are specified as members of the class of lal:ApplicationBeans. These are beans that are global to the whole application, and are around for the lifetime of the application. This is annoying at times - I need something akin to a session bean, I think - especially when you want to load beans in response events; for information about how to do this, see the section on TemporaryBeans. The lal:requires links are used to determine in what order to load and initialize the beans. In order to support cycles, these are two stages. The loading process simply places a new instance of each bean class into a lookup table, as per the lal:className property. The initialization process examines each lal:setProperty property, and sets the properties to the appropriate value, which can either be a literal, another bean, or a property of another bean. There is a system for chaining property requests, so it is possible to get nested properties, although there is no specification for assigning them URIs or setting nested properties.

Finally, before the main frame is made visible, the menu is configured. The menu setup is much simpler than the bean setup. It simply attaches menu items to menus (or the Core bean), associating ActionListeners to the items. The ActionListeners must be retrieved from loaded ApplicationBeans.

The Triplestore

The RDF triplestore is held by a PrefsManager. In the current incarnation, it is a Jena2 RDF Model. As that doesn't offer a method for handling events, or, more appropriately, persistant queries. The preference manager bean also offers some helper methods, and gives a tiered system of maintaining the triples, while providing a unified store. The first tier is the System tier, loaded when the system starts from the lal.prefs file. The User tier is next, loaded from the user.n3 file in the user's application directory (user directory/.appname/user.n3). Next is the temporary tier, which is not serialized.

Although the preference manager provides access to the unified model, this will not generate events. Instead of accessing the jena Model directly, all changes must go through the changeModel methods on the PrefsManager. These methods take a set of triples to remove and a set of triples to add, in the form of jena models.

Changes you want to be persistent across runs of the application should be applied to the User model. The System model can be modified (this is what the Props2Prefs trigger handler does), but they won't be serialized, nor will the Temporary model. The User model is serialized when the program is exited, or when the serializeUserPrefs method of the preference manager is invoked. As such, the modified prefs are not saved in the event of an error or uncaught exception.

Things that might be useful: support for OWL (would mean that the information would be promoted to the stricter OWL model, which doesn't allow for things like setting properties to literals or objects) - this would allow easy enforcement of things like unique properties, e.g. allow changing an application's name w/o removing the old name. For example, this would allow the user.n3 a limited ability to remove triples stated in the system.n3 file. Another possibility would be specifiying a user-remove.n3 file that is loaded with the user.n3 file, but that might be overly verbose and allow the user.n3 file too much leeway to completely redo the application.

Declaring a Bean

There are two types of beans, Application and Temporary beans. A third (or fourth) type may be added later if required. Application beans are loaded at the start of the application, may be identified by URI, and are not removed until the application closes. Temporary beans must be referenced indirectly through properties of other beans or through the lal:Parent link, and are created when needed. The initialization procedure is similar for both: all bean properties are set, and then the visual representation is dealt with.

An lal:setProperty predicate identifies that a property of the bean should be initialized to a given value. The target must be an lal:BeanProperty resource with a lal:propertyName and lal:propertyValue links. The lal:propertyName's object must be the name of a property. If there is no props:interfacer specified (described in the Property Sheet section), then this label will be used to find the property setter method on the object (the <code>set<em>Name</em></code> method). The lal:propertyValue can be either a directly encoded value, as a typed literal (untyped will be interpreted as Strings), direct link to another ApplicationBean resource, the value lal:Parent, which refers to the context in which the property is invoked (in the case of an ApplicationBean's property, this is the bean itself; in TemporaryBeans, it usually has more meaning), another bean property with a lal:propertyOf link and an lal:propertyName, or some list of several of these for multiparameter property set methods, as in setSize on a Dimension object.

After the properties are loaded, beans with a graphical representation are displayed if requiested. Implementations of a JFrame are always loaded up, with the rdfs:label as their window title, while JComponents are not made visible unless attached to an existing JFrame or JComponent bean.

Declaring a Menu Item or Group

A menu item is like a bean, but is not implemented as such, at least currently (it would make some sense to unify the two). The menuing system has its own menu namespace and schema. A Menu item may be attached to another Menu, to a menu Group, or to a bean with a window as a visual representation. A menu attached to a window is instantiated within a JMenuBar. A Menu attached to another menu uses the standard JMenu class. The Groups do not show up as actual menus; these are used to provide JMenuSeperator-defined groupings inside a menu. (Theoretically, a future system could replace them with sub-menus when the list becomes too long.) The priorities are like Python array indexes: zero is the first element, -1 is the last element. A downside to this rule-like approach is that it can be difficult to debug, when menu items are out of order or not appearing where they should be.

For example:

:viewMenu
  a menu:Menu ;
  rdfs:label "View" ;          # the default text string
  rdfs:label "Regarde"@"fr" ;  # a localization
  menu:mnemonic "V" ;          # Hitting Alt+V will get this menu to come up
    menu:attachment lal:Core ; # The root window of any Limn3 app
  rdfs:comment "View menu, for controlling the user's perspective" ;
                               # the comment may be used for the tooltip
  menu:priority "2" .          # will come after items 0 (File), 1 (Edit)

:zoomGroup a menu:Group ;      # a group for zoom commands
  menu:attachment :viewMenu ;  # within the view menu
  rdfs:label "Zoom" ;          # this could be used later
  menu:priority "-1" .         # will be displayed last
     # groups are smart enough to not render a seperator if
     # none is required - e.g. only one group exists, or 
     # the group is empty.

:zoomToActual a menu:Item ;    # a menu item
      menu:attachment :zoomGroup ; # within the zoom group
      rdfs:label "Actual Size" ;   # the menu item text
      menu:generates :zoomActualSizeActionate ;
                                   # the event generated. This is a bean!
      menu:mnemonic "A" ;          # for blt-ing
      menu:priority "-20" .        # a priority should always be specified,
                                   # unique to each item with that attachment.

Idea: Item is a subclass of ApplicationBean. OWL may constrain the class for menus, elements and groups to the appropriate java classes. The problem with this is avoiding the application loader - which loads, or may load, all app beans on startup. This is probably a good thing, actually. The difficulty would be in having each bean know how to handle itself. The attachment property would imply a :requires link. So, this would require OWL or RDFS reasoning, or at least the code would be significantly simplified if we employed it. Right now, we should stick with the two seperate processes. If we add RDFS or OWL reasoning, we can switch to this method.

Localizing an Undo Event and Using the Action History

One of the useful extensions available with the apploader is the undo history manager. It gives a list of undoable events in the past, and presents an interface for undoing several at a time. There is also an additional interface for undoable events that offers a 'getURI' function, to identify undo event types by URI. These URIs can be used for localization, by assigning an undo:Describer to them.

Defining a Property Sheet

The property sheet control offered by the AppLoader toolkit can use either the standard javabean method of detecting properties or use an RDF description of the object. The second method allows more control over what is displayed, and allows objects to have extended properties on a per-instance basis.

Using the File History Manager

The file history manager bean doesn't have a user interface, but has methods for altering the file menu when files are accessed. It uses the User triplestore to keep its data.

Miscellanious Utilities

Enhanced Table

This presents a JTable that has some additional features. Right now, the only real feature is slightly higher-level event handling, with a "TableListener" that has events that include the row and column where the event occured, instead of having to use the point-lookup methods on the MouseEvent objects.

Flattened List

Presents a one-layer flattening of a sequence of lists: i.e. it treats several lists as a single, concatenated list without actually concatenating or copying anything.

MultiIterator

The companion to the Flattened List, this iterator takes several iterators and concatenates them.

AppLoader (Core bean) helper methods

The core bean provides several methods for accessing bean properties. These provide a uniform method for accessing javabean-style properties and explicit RDF-defined properties. There is also a method for boxing primitive types, something that Java1.5 should make unnecessary. Other useful methods include: a type tester that takes into account boxing primitives, a method for converting file names into URIs, and an accessor for the currently loaded application beans.

.._`lal:PreferenceFlag`: http://viper-toolkit.sourceforge.net/owl/apploader#PreferenceFlag .. Jena: http://jena.sourceforge.net/ .. lal:PreferenceTrigger: http://viper-toolkit.sourceforge.net/owl/apploader#PreferenceTrigger .. lal:abbr: http://viper-toolkit.sourceforge.net/owl/apploader#abbr .. rdf:label: http://www.w3.org/TR/rdf-schema/#label .. lal:longName: http://viper-toolkit.sourceforge.net/owl/apploader#longName .. lal:invokes: http://viper-toolkit.sourceforge.net/owl/apploader#invokes .. lal:inserts: http://viper-toolkit.sourceforge.net/owl/apploader#inserts .. lal:ApplicationBeans.: http://viper-toolkit.sourceforge.net/owl/apploader#ApplicationBean .. menu namespace and schema: http://viper-toolkit.sourceforge.net/owl/menu .. undo:Describer: http://viper-toolkit.sourceforge.net/owl/undo#Describer

Docutils System Messages

System Message: ERROR/3 (<string>, line 59); backlink

Unknown target name: "lal:preferenceflag".

System Message: ERROR/3 (<string>, line 59); backlink

Unknown target name: "rdfs:label".