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 system properties which can control the behavior of the tool. These can be defined when using gradle like so:
gradlew :module-rt:slotomatic -Dproperty.name(=value1,value2,valuen)
gradlew :module-rt:slotomatic -Dslotomatic.forceRecompile
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 -Dslotomatic.include=com/acme/driver/BAcmeDriver.java
# or
gradlew :module-rt:slotomatic -Dslotomatic.include=com.acme.driver.BAcmeDriver
You can specify multiple files as well, and wildcards are supported:
gradlew :module-rt:slotomatic -Dslotomatic.include=com.acme.driver.BAcmeDriver,com.acme.driver.BAcmePoint
# Include all classes in com.acme.driver
gradlew :module-rt:slotomatic -Dslotomatic.include=com.acme.driver.*
gradlew :module-rt:slotomatic -Dslotomatic.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 BObject
s 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
/**
* 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.
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 and do both steps at once, using the following command:
gradlew :module-rt:slotomatic -Dslotomatic.migrateBeforeRecompile
However, the migration task can be invoked without running a slot-o-matic compile:
gradlew :module-rt:migrateSlotomatc
Copyright © 2000-2019 Tridium Inc. All rights reserved.