Overview
Sedona is a component oriented language, which means that programs are developed as reusable chunks of code designed to be snapped together much like Lego building blocks. This chapter discusses components themselves; the Apps chapter discusses how components are assembled into applications.
A component is any class that extends
sys::Component
.
Components include several features:
- Can define property fields
- Can define action methods
- Can be organized into a tree structure
- Can be given a human friendly name (up to 7 ASCII characters)
- Have a two byte identifier within an application
- Can be linked within an application
- Provides callback methods that can be overridden
- Component types have reflection capability
Properties
Properties are normal instance fields that are annotated with
the property
keyword. Properties are how components
expose configuration and runtime state. Properties are restricted
to the following types:
bool
byte
short
int
long
float
double
sys::Buf
Note that you must use the := operator when assigning values to properties. See Assigning to Properties for details.
Config vs Runtime
Properties are either config or runtime. Config properties are persistent and typically are changed by the user. Runtime properties are transient and are changed by the component itself during runtime. Properties are runtime by default unless marked with the "config" facet. A simple example:class FooServer extends Component { @config property bool enabled @config property int port = 80 property int count // runtime prop }
Buf Properties
Properties typed as Buf
are a bit special from
the primitive property types. The biggest difference is that
primitives are by-value and can't be changed without setting
the field itself. However, buffer properties are
always inlined and accessed by reference. When declaring
a buffer property, the inline
modifier
is implied. The following example illustrates a buffer property
with a total capacity of 8 bytes:
class Foo extends Component { property Buf(8) blob }
Because buffer properties are accessed by reference, the framework
has no knowledge when the buffer is modified. So it is the developer's
responsibility to notify the framework of a buffer change via
the Component.changed
method (typically using
a slot literal):
void updateBlob() { blob.copyFromStr("wow!") changed(Foo.blob) }
Note that changed
is called automatically when
setting primitive property types. You should only manually
call changed
for Buf
properties.
AsStr Properties
Buf
is the only non-primitive property type
available. However, it is a common case to store a Str
value in a Buf
property. If you know that a buffer
property will always store a null-terminated string, then you
should mark the property with the "asStr" facet:
class Foo extends Component { @config @asStr property Buf(8) descr }
The Buf
size is the total capacity including
the null terminator - so in the example above we can store a
string with a max of seven characters. The Buf
class has convenience methods when storing a string such as
toStr
and copyFromStr
.
Marking a buffer property asStr has a couple of benefits:
- Buffer overruns are handled to ensure that there is always a null terminator during deserialization.
- It lets higher levels of the framework treat the buffer as more than raw binary data. For example, asStr properties are serialized as text rather than base64 in XML files.
Actions
Actions are normal instance methods that are annotated with the
action
keyword. Every action is implicitly defined to be virtual
,
so they can be overridden. Actions are typically used as commands on a
component. As methods, actions "do something" rather than store or
display a value. Actions methods must be declared to return void and
must have zero or one parameter. If a parameter is specified then it
must be one of the following types:
bool
int
long
float
double
sys::Buf
Examples of "actions in action":
class Foo extends Component { action void actionNoArg() { Sys.out.print("actionNoArg").nl() } @asStr action void actionStr(Buf x) { Sys.out.print(x.toStr()).nl() } } class FooOverride extends Foo { override action void actionNoArg() { Sys.out.print("Override actionNoArg").nl() } }
Note that you can use the "asStr" facet to annotate an action that takes a Buf, if the argument should be a null-terminated string.
Callbacks
Any class that extends sys::Component
includes the following virtual methods.
Each is called automatically by the system at the appropriate time.
These callback methods may be overridden by
Component subclasses to change the behavior of the component.
virtual void loaded() virtual void start() virtual void execute() virtual void changed(Slot slot) virtual void setToDefault(Slot slot) virtual int childEvent(int eType, Component child) virtual int parentEvent(int eType, Component parent) virtual int linkEvent(int eType, Link link)
See the API documentation at sys::Component
for details on how these methods are used.