The Slot-o-matic tool is a gradle plugin that generates Java code for Baja slots based on class-level annotations on a Baja object. The generated code is placed in the same source file, and none of the other code is changed in any way
Slot-o-matic is invoked by gradle in a similar way to how you compile your Java code:
gradlew :module-rt:slotomatic
This will process all Java files in your module that contain slot annotations and generate code for them. Slot-o-matic will compile any Java file that meets the following conditions:
By default, Slot-o-matic will not compile files that have not changed since the last time the tool was run. This can be changed with properties as outlined below.
When invoking Slot-o-matic on your project, there are command line arguments that can control the behavior of Slot-o-matic. These can be provided as below:
gradlew :module-rt:slotomatic --option1 --option2=something --option3
A full list of the options for this (or any other) Gradle task can be found by running the help command:
gradlew help --task :module-rt:slotomatic
gradlew :module-rt:slotomatic --force
When run with this property set, Slot-o-matic will recompile any files it comes across, even if they have not been changed since the last time Slot-o-matic was run.
Slot-o-matic allows you include (or exclude) specific files, folders, classes, or packages when running. This is especially useful when dealing with a large codebase consisting of a mix of legacy and annotation-based slot code.
All include and excludes are relative to the source root of your module: src for code and srcTest for data.
For example, to only run Slot-o-matic on one file:
gradlew :module-rt:slotomatic --include com/acme/driver/BAcmeDriver.java
# or
gradlew :module-rt:slotomatic --include com.acme.driver.BAcmeDriver
You can specify multiple files as well, and wildcards are supported:
gradlew :module-rt:slotomatic --include com.acme.driver.BAcmeDriver,com.acme.driver.BAcmePoint
# Include all classes in com.acme.driver
gradlew :module-rt:slotomatic --include com.acme.driver.*
gradlew :module-rt:slotomatic --include com/acme/driver/*
Excludes work in the same way, but have the effect of re-slotting everything but files matching the exclude pattern
Starting with Niagara 4, developers can use Java annotations on their Baja classes to indicate their slots and other information used by the Niagara runtime environment. All supported annotations and their usage are detailed below.
There are a few caveats/notes which apply to all Niagara annotations.
There are three types of annotations defined by the Java language. You may see references to these different annotation types in this document:
String, Class, enum, and other annotations, as well as arrays of these types. We cannot, for example, have a BObject as an attribute for an annotation, so we must instead specify a String and create the BObject later.Unless otherwise noted, all attributes of “String” type must be fully-escaped String literals. You cannot use String constants, even if they are in the same class that is being annotated. Although this will appear to work, Slot-o-matic will not parse them correctly and you will end up with incorrect code.
If quotation marks are used in a String, They need to be escaped as such:
defaultValue="BString.make("Hello World")" // will not work because the quotes can not be parsed
defaultValue="BString.make(\"Hello World\")" // will work because the quotes have been escaped
Nested quotes must also be escaped properly:
defaultValue="BString.make(\"He said, \"Hello\" to her.\")" // will not work
defaultValue="BString.make(\"He said, \\\"Hello\\\" to her.\")" // will work
@NiagaraType(
agent = {
@AgentOn(types={"baja:MyNiagaraType", "baja:AnotherType"}, requiredPermissions="w", app="someApp", defaultAgent=Preference.PREFERRED),
@AgentOn(types="baja:YetAnotherType"),
},
adapter=@Adapter(from="fromInstance", to="toInstance"),
ext={@FileExt(name="myFileExt")},
ordScheme="myOrdScheme"
)
The @NiagaraType annotation is the most important annotation and is the only annotation that all Niagara types must have, even those that do not declare any slots. A @NiagaraType annotation is necessary for the automatic update of module-include.xml and moduleTest-include.xml.
Unlike the other Niagara annotations, @NiagaraType is processed at compile time by an annotation processor and not by Slot-o-matic. This compile-time processing is responsible for updating the module-include.xml or moduleTest-include.xml files with the correct Baja type information used by the system registry.
@NiagaraType is a normal annotation, but all of its attributes are optional. If none of the attributes are needed,@NiagaraType can be treated as a marker annotation.
The @NiagaraType annotation has several attributes, detailed below.
The agent attribute defines which class, if any, the BObject annotated with @NiagaraType is an agent on. This attribute is an array of @AgentOn annotations, and is optional.
@AgentOn
(
types="module:TypeName", //The target types that the agent is registered to. This is a String array
requiredPermissions="w", //The required permissions could be "r", "w", "rw", or not used at all.
app="myAppName", //influence on the registry to set the correct default agent and remove the agents that do not mix well their their application.
defaultAgent=Preference.PREFERRED //Chooses where the agent is the preferred default. Options are: Preference.PREFERRED, Preference.NORMAL, Preference.NOT_PREFERRED
)
Since Annotations cannot contain actual BObjects as attributes, the @AgentOn annotation wraps information about what Baja types a class is an agent on.
A @NiagaraType annotation may have any number of @AgentOn annotations. Additionally, @AgentOn annotations can declare a type as an agent on multiple types.
An @AgentOn annotation has four attributes:
The adapter attribute specifies a class as an “Adapter” from a class to a different class. An adapter converts instances of one type to instances of another type. This attribute is a single @Adapter annotation, and is optional.
@Adapter
(
from="nameOfFromType",
to="nameOfToType"
)
An @Adapter annotation defines the source type and target type of an adapter class. It has two attributes:
If a given class models a file type, this attribute lists the extensions of the file types it models. This attribute is an array of @FileExt annotations, and is optional
@FileExt
(
name="extName" //The name of the file extension
)
A @FileExt annotation defines a single file extension. It has a single attribute, “name”, which is a String representing the file extension.
The ordScheme attribute is a single String which defines the ord scheme used by the class. It is optional.
Slot annotations are annotations which define the frozen slots of a Baja class. These annotations are processed by the Slot-o-matic tool to automatically generate the necessary fields and methods for a Baja class.There are three slot annotations, one for each slot type. A class may have any number of these slot annotations.
As mentioned, annotations cannot contain attributes that are Java Objects. As such, annotations were created which serve as data providers for the slot annotations where primitives and String types are not sufficient. These annotations cannot be applied to a class, but are used as attributes for the class annotations listed below.
@Facet(name = "BFacets.MIN", value = "3")
@Facet("BUtilityClass.getFacetList()")
A @Facet annotation describes a single slot facet, either as a key/value pair or as a simple value. It has two attributes:
"BFacets.xxx") or arbitrary value. This name must be a String literal (e.g., wrapped in ""), even if the value it refers to is also a String literal.BFacets.make()In addition to slot-specific attributes, all of the slot annotations have the following attributes:
Flags.FLAG constants is allowed and encouraged.@Facet annotations, each describing one a facet for this slot. Optional.false./**
* This is my property. This comment will be included in the slot definition. Please note that this is a javaDoc comment.
*/
@NiagaraProperty
(
name = "myFirstProperty", //REQUIRED Name of the property
type = "baja:RelTime", //REQUIRED Property type. Supports "Module:Type" typespec format (preferred) or full name.Example: "baja:RelTime" or "BRelTime" are valid.
defaultValue = "BRelTime.makeHours(1)", //REQUIRED The default value of the property
flags= Flags.HIDDEN | Flags.ASYNC, //Any flags the property my have. Notice that multiple flags are grouped together with the "|" symbol.
facets = //Any facets that the property may have. The facet annotation works as a name-value pair.
{
@Facet(name = "BFacets.MIN", value = "BRelTime.makeSeconds(0)"),
@Facet(name = "BFacets.MAX", value = "BRelTime.makeSeconds(60)")
}
)
A @NiagaraProperty annotation defines a single Property on a BComplex. A BComplex can have any number of properties, each declared in a separate @NiagaraProperty annotation.
A @NiagaraProperty annotation has the following attributes, in addition to the standard attributes detailed above:
/**
* This is my action. This comment will be included in the slot definition. Please note that this is a javaDoc comment.
*/
@NiagaraAction
(
name = "myOnlyAction", //REQUIRED Name of the action.
flags = Flags.HIDDEN, //Any flags the action my have. Note that multiple flags are grouped together with the "|" symbol.
parameterType = "baja:RelTime", //Parameter type of the action. Supports "Module:Type" typespec format (preferred) or full name. Example: "baja:RelTime" or "BRelTime" are valid.
defaultValue = "BRelTime.makeSeconds(5)", //The default value of the action.
returnType = "baja:RelTime", //Return type of the action. Supports "Module:Type" typespec format (preferred) or full name. Example: "baja:RelTime" or "BRelTime" are valid.
facets = //Any facets that the action may have. The facet annotation works as a name-value pair.
{
@Facet(name = "BFacets.MIN", value = "BRelTime.makeSeconds(0)"),
@Facet(name = "BFacets.MAX", value = "BRelTime.makeSeconds(60)")
}
)
A @NiagaraAction annotation defines a single Action on a BComplex. A BComplex can have any number of actions, each declared in a separate @NiagaraAction annotation.
A @NiagaraAction annotation has the following attributes, in addition to the standard attributes detailed above:
/**
* This is my topic. This comment will be included in the slot definition. Please note that this is a javaDoc comment.
*/
@NiagaraTopic
(
name = "myFirstTopic", //REQUIRED Name of the topic.
flags = Flags.USER_DEFINED_1, //Any flags the topic my have. Note that multiple flags are grouped together with the "|" symbol.
eventType = "int", //Event type of the topic. Supports "Module:Type" typespec format (preferred) or full name. Example: "baja:RelTime" or "BRelTime" are valid.
facets = //Any facets that the topic may have. The facet annotation works as a name-value pair.
{
@Facet(name = "BFacets.MIN", value = "1"),
@Facet(name = "BFacets.MAX", value = "40")
}
)
A @NiagaraTopic annotation defines a single Topic of a BComplex. A BComplex can have any number of topics, each declared in a separate @NiagaraTopic annotation.
A @NiagaraTopic annotation has the following attribute, in addition to the standard attributes detailed above:
@NiagaraEnum
(
range = {
@Range("zero"), // Let Slot-o-matic number these
@Range("one"),
@Range(value="two"), // Can explicitly use value as the key, if needed
@Range(value="three", ordinal=3), // Must explicitly use all keys if specifying ordinal
@Range(value="four", ordinal=5)
},
defaultValue = "one" // If this was not present, the default would be "zero"
)
A @NiagaraEnum annotation specifies that a given class is an enum. Classes annotated with @NiagaraEnum should extend from BFrozenEnum, but this is not enforced by Slot-o-matic. A class may only have one @NiagaraEnum annotation, and it may not have both a @NiagaraEnum annotation and any other Niagara annotations besides @NiagaraType
A @NiagaraEnum annotation has two attributes:
@Range annotations, each one specifying a single element in the enum’s range. This is required.A @Range annotation is used by the @NiagaraEnum to build up its range list. It has two attributes:
@Range annotation to be treated as a Single annotation if the ordinal is not needed.A range annotation can be specified as just @Range("name") if auto-numbering is necessary, or can be specified with an ordinal as @Range(value="name", ordinal=5). However,@Range("name", ordinal=5) is not valid syntax.
@NiagaraSingleton
A @NiagaraSingleton annotation specifies that a given class is a singleton class. The presence of this annotation will cause Slot-o-matic to emit a public static final INSTANCE field in addition to the TYPE field.
@NiagaraSingleton is a marker annotation with no attributes
@NoSlotomatic
A @NoSlotomatic annotation turns off any Slot-o-matic processing for a given type. This is useful for some types (such as certain BSimple subclasses). This should be used sparingly and as a feature of last resort, but it is available. Note that even when using this annotation, you must also use @NiagaraType.
@NoSlotomatic is a marker annotation with no attributes
/**
* Test input for multiple annotation slot code
*/
@NiagaraType
/** foo is the property used for stuff */
@NiagaraProperty(
name = "foo",
type = "String",
defaultValue = "sfafasf"
)
/** bar is another property used for stuff.
* It's more complicated, so the javadoc takes more lines
* or something like that
*/
@NiagaraProperty(
name = "bar",
type = "BString",
defaultValue = "BString.make(\"bar\")",
flags = Flags.HIDDEN | Flags.TRANSIENT
)
/** This action will cause things to happen. Probably. */
@NiagaraAction(
name = "things",
returnType = "int"
)
/** This event is fired when bar changes */
@NiagaraTopic(
name = "barChanged",
eventType = "BString",
flags = Flags.HIDDEN
)
public class BMultiAnnotationFoobar extends BComponent
{
}
/**
* Simple Enum class
*/
@NiagaraType
@NiagaraEnum(
range = {
@Range("deny"),
@Range("sameorigin"),
@Range("any"),
@Range(value="none", ordinal=5)
},
defaultValue = "sameorigin"
)
public class BEnumTest extends BFrozenEnum
{
}
/**
* Singleton class
*/
@NiagaraType
@NiagaraSingleton
public class BSingletonTest extends BSingleton
{
}
Note the generated code included in this example does not have any get, set, fire, or invoke methods generated for these slots
/**
* Test slot override mechanism
*/
@NiagaraType
@NiagaraProperty(
name = "overrideProp",
type = "String",
defaultValue = "Foo",
flags = Flags.HIDDEN,
override = true
)
@NiagaraTopic(
name = "overrideTopic",
override = true
)
@NiagaraAction(
name = "overrideAction",
flags = Flags.HIDDEN,
override = true
)
public class BTestSlotOverride
extends BMyClass
{
/*+ ------------ BEGIN BAJA AUTO GENERATED CODE ------------ +*/
/*@ $slotomaticModule.annotationTests.BTestSlotOverride(1213737594)1.0$ @*/
/* Generated Fri Sep 11 09:16:22 EDT 2015 by Slot-o-matic (c) Tridium, Inc. 2012 */
////////////////////////////////////////////////////////////////
// Property "overrideProp"
////////////////////////////////////////////////////////////////
/**
* Slot for the {@code overrideProp} property.
* @see #getOverrideProp
* @see #setOverrideProp
*/
public static final Property overrideProp = newProperty(Flags.HIDDEN, "Foo",null);
////////////////////////////////////////////////////////////////
// Action "overrideAction"
////////////////////////////////////////////////////////////////
/**
* Slot for the {@code overrideAction} action.
* @see #overrideAction()
*/
public static final Action overrideAction = newAction(Flags.HIDDEN,null);
////////////////////////////////////////////////////////////////
// Topic "overrideTopic"
////////////////////////////////////////////////////////////////
/**
* Slot for the {@code overrideTopic} topic.
* @see #fireOverrideTopic
*/
public static final Topic overrideTopic = newTopic(0,null);
////////////////////////////////////////////////////////////////
// Type
////////////////////////////////////////////////////////////////
@Override
public Type getType() { return TYPE; }
public static final Type TYPE = Sys.loadType(BTestSlotOverride.class);
/*+ ------------ END BAJA AUTO GENERATED CODE -------------- +*/
}
In Niagara AX, a different format for Slot-o-matic code was used. This format used a special Java comment block with additional characters to indicate the presence of a Baja comment block. While this syntax will continue to be supported in N4, it is highly recommended that developers migrate to using the new Annotation syntax. Baja comment block syntax will not receive any new features (including slot overrides).
To make the task of migrating old Slot-o-matic code easier, the Slot-o-matic tool can automatically migrate existing Baja comment block code to Annotation code.
Additionally, the migration can also add @NiagaraType annotations to types that are present in module-include.xml or moduleTest-include.xml but that do not have any Baja comment block code.
While the migration process is robust, there are still several caveats to keep in mind when running the Slot-o-matic migration tool:
The migration is a two-step process:
The recommended approach is to perform a two-pass compile:
gradlew :module-rt:migrateSlotomatic --import
gradlew :module-rt:slotomatic
The --import flag is also required to inject @NiagaraType on any types defined in module(Test)-include.xml. This is recommended as adding @NiagaraType to all types allows for fully automatic generation of module(Test)-include.xml.