BComplex
is the base class for both
BStruct
and BComponent
.
Classes never subclass
BComplex
directly (it doesn't support any public or protected constructors).
Rather developers subclass from BStruct
or BComponent
depending on their needs. In general structs are used as complex
data types. BStructs
can be built only using frozen
properties. BComponents
support much more flexibility and
are built using frozen and dynamic slots of all types:
BStruct | BComponent | |
---|---|---|
Frozen Property | X | X |
Frozen Action | X | |
Frozen Topic | X | |
Dynamic Property | X | |
Dynamic Action | X | |
Dynamic Topic | X |
As you will learn, BComponents
are also the basis
for many other features such as BOrds
, links,
and the event model. You may wonder why you would use a
BStruct
? There are two main reasons. The first is
that because of its limited feature set, it is more memory efficient.
The other reason is that properties containing BComponents
cannot be linked, but BStructs
can be (see
Links).
All concrete subclasses of BComplex
must meet the following
requirements:
BObjects
;
We have discussed how frozen slots are defined at compile time. Let's take a look at how the framework knows when frozen slots have been declared. Every slot is composed of two or three Java members. A member is the technical term for a Java field, method, or constructor. At runtime the framework uses Java reflection to examine the members of each class, looking for patterns to self-discover slots. These patterns are based on the patterns used by JavaBeans, with significant extensions. Remember introspection is used only to define frozen slots, dynamic slots are not specified in the classfile itself. There is a different pattern for each slot type.
These introspection patterns require a fair amount of boiler plate code. Although it is not too painful to write this code by hand, you may use Slot-o-matic to generate the boiler plate code for you.
Every frozen property must follow these rules:
public static final Property
field
where the field name is the property name.BComplex.newProperty()
method. This
method takes a set of flags for the property, and a default
value.type getCapitalizedName()
.void setCapitalizedName(type v)
.BObject.get(Property)
. The method
must not perform any addition behavior.BObject.set(Property, BObject)
.
The method must not perform any additional behavior.BValue,
boolean, int, long, float, double,
and String
. The six non-BValue
types have special accessors which should be used in the getter and
setter implementations.
The introspection rules map Property meta-data as follows:
newProperty()
.newProperty()
.The following illustrates an example for different property types:
// boolean property: fooBar
public static final Property fooBar = newProperty(0, true);
public boolean getFooBar() { return getBoolean(fooBar); }
public void setFooBar(boolean v) { setBoolean(fooBar, v); }
// int property: cool
public static final Property cool = newProperty(0, 100);
public int getCool() { return getInt(cool); }
public void setCool(int v) { setInt(cool, v); }
// double property: analog
public static final Property analog = newProperty(0, 75.0);
public double getAnalog() { return getDouble(analog); }
public void setAnalog(double v) { setDouble(analog, v); }
// float property: description
public static final Property description = newProperty(0, "describe me");
public String getDescription() { return getString(description); }
public void setDescription(String x) { setString(description, v); }
// BObject property: timestamp
public static final Property timestamp = newProperty(0, BAbsTime.DEFAULT);
public BAbsTime getTimestamp() { return (BAbsTime)get(timestamp); }
public void setTimestamp(BAbsTime v) { set(timestamp, v); }
Every frozen action must follow these rules:
public static final Action
field
where the field name is the action name.BComponent.newAction()
method. This
method takes a set of flags for the action and an optional
default argument.void
or a BObject
type. This method must take zero or one parameters. If it takes
a parameter, it should be a BObject
type.BComponent.invoke()
. No other behavior is permitted
in the method.
The introspection rules map Action meta-data as follows:
newAction()
.The following illustrates two examples. The first action contains neither a return value nor an argument value. The second declares both a return and argument value:
// action: makeMyDay
public static final Action makeMyDay = newAction(0);
public void makeMyDay() { invoke(makeMyDay, null, null); }
public void doMakeMyDay() { System.out.println("Make my day!"); }
// action: increment
public static final Action increment = newAction(0, new BInteger(1));
public BInteger increment(BInteger v)
{ return (BInteger)invoke(increment, v, null); }
public BInteger doIncrement(BInteger i)
{ return new BInteger(i.getInt()+1); }
Every frozen topic must follow these rules:
public static final Topic
field
where the field name is the topic name.void fireCapitalizedName(EventType)
.BComponent.fire()
. No other behavior is permitted
in the method.The introspection rules map Topic meta-data as follows:
newTopic()
.The following code example illustrates declaring a frozen topic:
// topic: exploded
public static final Topic exploded = newTopic(0);
public void fireExploded(BString event) { fire(exploded, event, null); }
Dynamic slots are not declared as members in the classfile, but
rather are managed at runtime using a set of methods on
BComponent
. These methods allow you to add, remove,
rename, and reorder dynamic slots. A small sample of these
methods follows:
Property add(String name, BValue value, int flags);
void remove(Property property);
void rename(Property property, String newName);
void reorder(Property[] properties);
Note: You will notice that methods dealing with dynamic slots
take a Property, not a Slot. This is because all dynamic slots
including dynamic Actions and Topics are also Properties. Dynamic
Actions and Topics are implemented by subclassing BAction
and BTopic
respectively.
Copyright © 2000-2019 Tridium Inc. All rights reserved.