UIEvolutin Inc.
UJML Language Reference
Components

A component consists of related variables, functions, state transitions, and templates which are compiled into a single byte code file.

A UJML application consists of variables, state transitions, and functions co-operating to produce a desired outcome. However, these varied parts may be disjointed and unrelated from each other except by the fact they are all part of the same application. See Anatomy of a UJML Application. For large programs, this can lead to complex and difficult-to-follow code (also known as 'spaghetti code'). 

To solve this problem, UJML provides 'state machines' and 'components'; which you can think of as a package of related variables, functions, and state transitions. See State Machines. Both state machines and components increase modularity and simplify code. Both, used properly, allow you to create reusable function libraries which you can apply to different applications without modification. However state machines are included into UJML source when it is compiled and components are compiled into separate byte code files. At run time each state machine is a single unit of code loaded along with its containing application, partition, or state machine. Components, on the other hand, may be loaded as separate 'instances' as many times as needed. 

Unlike state machines, only functions in a component may be public and they must implement an interface member before they may be invoked. See Interfaces. Besides exposing interface members, components may also fire public events that other code can handle. See Component Events

Components are similar in many ways to objects in an object oriented programming, but do not provide all of the attributes required for a truly object oriented programming language. See Components and Object Oriented Programming.

Declaring a component

Components are declared with a component element contained by a ujml element. See component element, ujml element. Each component element contains child state machines, variables, functions, templates, and state transitions. Components also contain public functions and can raise events, so long as those functions and events are described in an interface definition. See Interfaces. This is called 'implementing' the interface.

Implementing interfaces

Public functions of a component must implement the members of a declared interface. See Interfaces. The particular member of an interface implemented by a component function is indicated in the component function name using the interface name and member name in 'dot notation'. For example 'InterfaceName.memberName'

A component may implement members of more than one interface. Which interfaces a component implements is determined by the components declared inside of an interfaces element contained by the component element. See interfaces element. Inside of the interfaces element are one or more interface element, each specifying an interface name and an access level of 'import' or 'export'. See interfaces element, interface element. Imported interfaces may be accessed by code in the component, but not exposed through public functions. Exported interfaces are exposed via public functions 

Note: All members of an exported interface must be exposed via the public functions of a component or an error will occur at compile time.

Component files

Components must be declared in separate UJML files. See Component Files. Each component file contains one component declaration and is compiled into a byte code file of the same name. See Byte Code Files.

Using a component

Before you can use a component you must bring it into memory with a resource element and create an 'instance' of it using the _createInstance() function. See Component Resources, _createInstance() function. Components instances are stored in reference variables. See reference data type. Your code may only access a component instance through through an interface type variable. See Interface Types. You access an interface member of a component using the variable name and the member name in 'dot notation'. For example 'variableName.memberName()'. The variable must be of the same interface type as the interface member you are invoking. See Interface Types.

Displaying a component

A Component with visual elements is displayed in a state transition using a display-instance element contained by a display element. See Visual Components, display-instance element, display element. The visual Component elements will only be visible on the device screen while the transition is active, even if the elements are in an active transition inside the Component. See State Transitions.

Casting component instances

The _castInterface() function converts a reference value to a specific interface type. See _castInterface() function.

Comparing component instances

You can compare two component instance references to see if they refer to the same component instance using the _isSameInstance() function. See _isSameInstance() function.

Reflecting component interfaces

You can determine if a component instance implements a specific interface using the _instanceOf() function. See _instanceOf() function.

Component instances and 'null'

Interface type references may be a special value called 'null'. When a reference is null, it does not refer to any extension instance. It is, essentially, empty. You can test to see if a reference is null by using the _is_null() function. See _is_null() function. You can also assign the literal value 'null' to a reference variable to give the variable the value null.

Reference literals

Because component references are an opaque type, there is no such thing as a reference literal except for the value 'null'. See Literals and Constants.

Component design tips

To most efficiently use UJML components you should design your Interfaces to provide fine-grained functionality, with all the members of the interface in some way contributing to a single 'mission' for the interface. However your components should be large-grained, providing as much functionality as possible in a single implementation. To do this you will often want to implement multiple interfaces for each component. 

Your code should take advantage of the polymorphism provided by interface types via each part of the code only knowing about the interfaces it needs to do its job. However casting interfaces is potentially an expensive operation and should be avoided in process-intensive situations. 

The Things and Life samples both rely on multiple interface implementation and polymorphism to work. The Things sample makes heavy use of polymorphism and dynamic casting. The Life sample illustrates some of the trade-offs between implementing large numbers of lightweight objects as opposed to a single heavyweight object; with the heavyweight object, strangely enough, earning a 'light' designation because it makes more efficient use of device resources.

Topic 
Description 
An Interface is a description of the public services (members) implemented by a Component. 
Components can fire events for external code to handle, similar to built-in UJML events. 
Components offer some object oriented programming advantages, but do not provide true OOP. 

The following example shows how to create an instance of the 'LifeGrid' component and put it into a reference variable. It is part of the life.ujml sample. See also lifegrid.ujml sample.

mGrid = _createInstance("LifeGrid");

 

The following example shows how to export two interface declarations. It is part of the animal.ujml sample.

<interfaces>
    <interface name="IThing" access="export"/>
    <interface name="IAnimal" access="export"/>
</interfaces>

 

The following example shows how to implement a member of the IAnimal interface. It is part of the animal.ujml sample.

<function name="IAnimal.getSound" type="string">
    <return><eval>mStringVal</eval></return>
</function>

 

The following example shows a complete component declaration. It is part of the unknown.ujml sample.

<ujml>
    <interfaces>
        <include file="ithings.ujmi"/>
    </interfaces>

    <component name="Unknown">
        <interfaces>
            <interface name="IThing" access="export"/>
            <interface name="IUnknown" access="export"/>
        </interfaces>

        <variables>
            <var name="mName" type="string"/>
            <var name="mDescription" type="string"/>
            <var name="mImageName" type="string"/>
        </variables>

        <functions>
            <!--
                Gets the name of the thing.
            -->
            <function name="IThing.getName" type="string">
                <return><eval>mName</eval></return>
            </function>

            <!--
                Gets a description of the thing.
            -->
            <function name="IThing.getDescription" type="string">
                <return><eval>mDescription</eval></return>
            </function>
            <!--
                Gets the image URL of the thing.
            -->
            <function name="IThing.getImageName" type="string">
                <return><eval>mImageName</eval></return>
            </function>

            <!--
               Initializes the thing.
            -->
            <function name="IUnknown.init" type="void">
                <parameters>
                    <var name="name" type="string"/>
                    <var name="description" type="string"/>
                    <var name="imgName" type="string"/>
                </parameters>
                <script>
                    mName = name;
                    mDescription = description;
                    mImageName = imgName;
                </script>
            </function>
        </functions>
    </component>
</ujml>
Copyright (c) 2000-2007 UIEvolution, Inc. All rights reserved.
What do you think about this topic? Send feedback!