There are several inadequacies in the Baja Collections API - javax.baja.collection and javax.baja.sys.Cursor. The current API suffers from a number of problems that hinder performance and encourage inefficient implementations for cases where data sets are large. The API changes aim to help developers be more productive with the collection API, and to pave the way for better implementation when underlying data sets are large.
Any module that makes use of the javax.baja.collection
classes or javax.baja.sys.Cursor
is impacted by these changes. Depending on what methods and classes of the API the code uses you may need to refactor your code. Any implementations of BICollection
, BIList
, and BITable
will be impacted by these changes. In the unlikely event that you implemented the javax.baja.bql.BIRelational
interface in your code, you will also be impacted.
One of the biggest issues with Niagara AX’s Collections API is the BICollection
interface. BICollection
requires every implementation to model itself as a collection, a list, and a table. This puts a heavy burden on developers implementing a collection, and in many cases it does not make sense to model a list as a table, and vice-versa. So this interface has been removed entirely.
The interface only had methods for converting the underlying collection to a list or table. Every implementation of BICollection
in the framework now implements BITable
. If you were casting objects to BICollection
you should be able to safely cast them to BITable
now. Almost invariably this was due to ord resolution of a bql query:
Niagara AX
BICollection result = (BICollection)BOrd.make("bql:select displayName").get(base);
BITable table = result.toTable();
Niagara 4
BITable table = (BITable)BOrd.make("bql:select displayName").get(base);
Any public methods that took a BICollection
will need to be refactored to expect a BITable
.
This change is probably the most significant in terms of fundamental philosophy change. As part of the design philosophy for collections in Niagara 4, we wanted to discourage random-access methods. In fact, they have essentially been removed from the collection API in favour of cursor-based access. Don’t worry, you can still work with a table in a random-access way (details below). The ‘BIList’ interface essentially required random-access support for every collection. Further, an analysis of the entire framework showed that there were zero concrete implementations of BIList
/BICollection
in the public API that did not also implement BITable
. This indicates that the BITable API is more useful to the framework as a whole.
Similar to BICollection
above, you should be able to cast any reference to a BIList
to a BITable
now. If by chance you had a public method that expected a BList
, you will need to refactor that API to take a BITable
.
The BITable interface has been greatly simplified and all random-access methods have been removed.
You can iterate the rows in the table by obtaining a TableCursor. The TableCursor
gives you access to the table that contains the row, the Row object itself (see below), and a convenience method to obtain a cell value for the current row.
Each row in a table is modelled as a Row
object. The row object gives you direct access to the underlying BIObject backing the row, as well as column cell values, flags, and facets.
The biggest impact will occur if your code was iterating a BITable
using the random-access methods of the old API. You have a few options.
First, change your code to iterate the table using a cursor. This is the best option.
// Iterate a BITable using a TableCursor
//
BITable table = (BITable)bqlOrd.resolve().get();
Column[] columns = table.getColumns().list();
try(TableCursor<BIObject> cursor = table.cursor())
{
// Just for printing purposes, not for random access.
int row = 0;
while (cursor.next())
{
System.out.print(row + ": ");
for (Column col : columns)
{
System.out.print(cursor.cell(col) + ", ");
}
System.out.println();
++row;
}
}
If you must access the table using random-access indexing, you can convert it to a BIRandomAccessTable using the javax.baja.collection.Tables
utility class.
BITable table = (BITable)ordThatResolvesToTable.resolve().get();
BIRandomAccessTable rat = Tables.slurp(table);
System.out.println(String.format("This table has %d rows", rat.size()));
for (int i=0; i<rat.size(); ++i)
{
Row row = rat.get(i);
// Do something with each row...
}
There are a few major changes to the javax.baja.sys.Cursor
interface.
java.lang.AutoCloseable
. This means you should be a good citizen of every cursor you work with. Failing to close a cursor may result in a resource leak and degraded system performance. The try-with-resources statement introduced in Java 7 can help manage opening and closing cursors.Cursor
is now generic: public interface Cursor<E> extends AutoCloseable
SlotCursor
. This seems to be its primary use case anyway.Iterable
interface. This enables a Cursor
to be used in a for each statement as well as accessing the Cursor
as an Iterator
, Spliterator
or Stream
.Iterable
interface so it can be used to iterate over a collection of Slots (not BValue).If you need to implement your own Cursor
, use the utility class javax.baja.collection.AbstractCursor, which stubs out all methods in the interface and handles close semantics for you. You only need to provide an implementation of advanceCursor()
and doGet()
.
Here are some example of the new SlotCursor
design that use Java 8’s Stream API…
// Remove all dynamic Properties from a point...
point.getProperties()
.stream()
.filter(Slot::isDynamic)
.forEach(point::remove);
// Print out the path string of all folders under a point...
point.getProperties()
.stream()
.map(point::get)
.filter(v -> v.getType().is(BFolder.TYPE))
.forEach(v -> System.out.println(v.asComponent().toPathString()));
In the unlikely event that you implemented the javax.baja.bql.BIRelational interface in your code, you will need to add a Context argument to its single method. The updated interface class is shown below:
public interface BIRelational<T extends BIObject>
{
/**
* Get the relation with the specified identifier.
*
* @param id A string identifier for the relation. The format
* of the string is implementation specific.
*
* @param cx The Context associated with this request.
* This parameter was added starting in Niagara 4.0.
*
* @return Returns the relation identified by id or null if the relation
* cannot be found.
*/
BITable<T> getRelation(String id, Context cx);
Type TYPE = Sys.loadType(BIRelational.class);
}
Copyright © 2000-2019 Tridium Inc. All rights reserved.