Implementing Design Elements
The entrypoint for accessing the database design is the DbDesign
interface in the domino-jnx-api
module, and the bulk of the implementation for that is in AbstractDbDesign
inside the domino-jnx-commons
moduke. Though there is a JNADbDesign
class in the domino-jnx-jna
module, that is just to provide a hook for an efficient C call to find elements - the bulk of the work should take place in domino-jnx-commons
to allow for future portability.
Entity Hierarchy
Note-backed Entities
The DesignElement
interface is the top-level interface for all note-based design elements, and its primary implementation is AbstractDesignElement
. In turn, there are a handful of more-specific and “mixin” interfaces/abstract classes to narrow down common behavior among design elements:
NamedDesignElement
represents a design element that has a developer-specified name, usually stored in the$TITLE
item. This means elements like Forms and Views as opposed to single-entity-only elements like the database script and the About and Using Documents.AbstractNamedDesignElement
provides the base implementation for thisNamedFileElement
represents elements that are conceptually files, such as File and Image Resources, Composite App elements, and XPages-related elements.AbstractNamedFileElement
provides the base implementation for thisXPageAlternativeElement
is a mixin interface to describe elements (Forms and Views) that have an “On web access, display XPage instead” optionXPageNotesAlternativeElement
is a mixin interface similar to above, but for elements with an “On client access, display XPage instead” option (admittedly, this is likely to remain confined to Forms and thus this interface may be removed in the future)ThemeableClassicElement
is a mixin interface to describe elements (Forms, Views, Framesets) that have an option to configure their Themeing behavior (which allows you to use an XPages Theme file to specify a handful of largely-undocumented aspects of these elements)
Related entities generally share a common superinterface and abstract representation. For example, Folder
and View
extend from CollectionDesignElement
, and all script-library types extend from ScriptLibrary
.
Component Entities
As needed, we create interfaces that don’t extend DesignElement
to cover individual parts of a larger design element, such as Field
as present in forms and subforms, or CollectionColumn
as present in views and folders.
Additionally, some elements use inner interfaces to provide access to large amounts of related attributes. For example, the CollectionColumn
interface (which is in com.hcl.domino.data
and not com.hcl.domino.design
for historical reasons) contains a SortConfiguration
interface that provides access to essentially what you see on the second tab of the Column properties dialog in Designer. Those settings don’t have an existence separate from the column, but it makes sense to group them in this way to make the CollectionColumn
interface more readable. In general, we don’t want to go too far with this, but it makes sense when there is a logical grouping of more than a few properties.
Utility Entities
ComputableValue
represents a value that in the Designer UI can be specified as either a static value or a formula. Examples include the name of the Twistie Image for a view column and the name of an included subformSubformReference
represents an older specialized implementation of the above and will likely be removed in the near future
Naming
In most cases, naming interfaces is straightforward, but some are difficult. For example, the shared interface of View
and Folder
is named CollectionDesignElement
because “collection” describes both of those, but we wouldn’t want to name it just Collection
and leave it ambiguous and conflict with the Java Collections Framework. We also don’t want to use DesignCollection
(which would be analogous to DesignAgent
, which in turn is so named because we already have an Agent
) because the Design Collection is its own thing in Domino.
In another case, GenericFormOrSubform
is so named because we don’t have something better to describe both without just saying Form
.
For implementations, we name abstract classes generally as Abstract(interface name)
, like AbstractNamedFileElement
. Complete implementations that exist in the domino-jnx-commons
module are generally named Default(interface name)
or (interface name)Impl
, while implementations in domino-jnx-jna
are generally named JNA(interface name)
. The naming mix in domino-jnx-commons
is likely to be resolved in one direction or the other in the future - for the moment, design elements are more commonly (interface name)Impl
.
Implementations
Subclasses of AbstractDesignElement
are constructed with a com.hcl.domino.data.Document
instance and use that for all or most of their access, and the high-level methods on that class should be preferred to using JNA directly when possible - and, indeed, it’s best to put these in domino-jnx-commons
until there’s an explicit need to move them “down” to domino-jnx-jna
. For example, String getComment()
is implemented as:
@Override
public String getComment() {
return this.doc.getAsText(NotesConstants.FILTER_COMMENT_ITEM, ' ');
}
Implementing Specialized Data Types
Though many of the fields in a design note are either basic types like TEXT or are composite data, some (particularly as you get to older elements) are unique formats. This is the case for Views and Folders with $ViewFormat
, for example. These are identified in the NSF by distinct type codes, and the reading of them from the back-end document is done in JNADocument
, in the List<Object> getItemValue(String itemName, NotesBlockIdStruct itemBlockId, NotesBlockIdStruct valueBlockId, Pointer valuePtr, int valueLength)
method. This method contains a large if/then block to cover the handled data types, and so adding a new one can be done by adding to this block. The value returned in this is largely up to the implementor, but it should ideally be something that can be named in the domino-jnx-api
or domino-jnx-commons
modules, at least as a compatible interface.
For example, the block to read $ViewFormat
is:
else if (dataTypeAsInt == ItemDataType.TYPE_VIEW_FORMAT.getValue()) {
DominoViewFormat viewFormatInfo = ViewFormatDecoder.decodeViewFormat(valueDataPtr, valueDataLength);
return Arrays.asList((Object) viewFormatInfo);
}
The ViewFormatDecoder
does a bunch of work and produces a DominoViewFormat
object, which is a class from domino-jnx-commons
. In turn, this is used by AbstractCollectionDesignElement
:
private synchronized DominoViewFormat readViewFormat() {
if (this.format == null) {
final Document doc = this.getDocument();
this.format = (DominoViewFormat) doc.getItemValue(DesignConstants.VIEW_VIEW_FORMAT_ITEM).get(0);
}
return this.format;
}
The ideal is for at least an interface to be present in domino-jnx-api
to account for cases where advanced API users may want to request these esoteric values, but that’s not a high priority at the moment. For now, it should at least be something from domino-jnx-commons
so that it’s implementation-neutral.
Constants
JNX admittedly doesn’t have a coherent strategy for organizing its constants brought over from the C header files. We do strive to represent them generally in interfaces, but there are a number of them involved, both for historic reasons and to avoid slowness in Java IDEs.
As constants are needed, we take their definitions from C and put them in an applicable interface, aiming to match the type as they are used. For example:
#define CAL_DISPLAY_CONFLICTS 0x0001 /* Display Conflict marks */
#define CAL_ENABLE_TIMESLOTS 0x0002 /* Disable Time Slots */
…converts to:
/** Display Conflict marks */
short CAL_DISPLAY_CONFLICTS = 0x0001;
/** Disable Time Slots */
short CAL_ENABLE_TIMESLOTS = 0x0002;
There are several buckets currently in use for these:
com.hcl.domino.misc.NotesConstants
is the oldest and largest, containing a great many constants from across the APIcom.hcl.domino.design.DesignConstants
is much smaller, and contains a handful of constants of use only in the Design APIcom.hcl.domino.misc.ViewFormatConstants
contains constants used specifically for view-format reading, largely from viewfmt.h in the C SDK
There used to be a partial distinction between these in that NotesConstants
was in domino-jnx-jna
while the others were in domino-jnx-commons
, but they’ve since all migrated up to domino-jnx-api
.
Structures
See rich_text_implementation.md for a discussion of JNX’s representation of most C structures as used in the domino-jnx-api
module.
Constant-Backed Enums
JNX has a mechanism for specifying “C-type” enumerations that are backed by constant values, which consists of the INumberEnum<...>
interface and the DominoEnumUtil
utility class. These can represent both “true” C enums with integer values as well as “bitmask” type multi-value enum fields used for things like “Flags” members of structs. For examples, see CDExtField
, which has both cases.
For naming, the convention is to use a Java-style class name for the enum itself (like Flag
) and then use a variant of the constant name without the leading disambiguating part for the actual enum constants. For example:
enum HelperType implements INumberEnum<Short> {
NONE(RichTextConstants.FIELD_HELPER_NONE),
ADDRDLG(RichTextConstants.FIELD_HELPER_ADDRDLG),
ACLDLG(RichTextConstants.FIELD_HELPER_ACLDLG),
VIEWDLG(RichTextConstants.FIELD_HELPER_VIEWDLG);
/* snip */
}
Though it’s nice when the Java class name matches the internal constant prefix, it can be good in cases like the above to choose a different name either for disambiguation, existing-class-similarity, or general better-naming purposes.
These are useful outside of “struct” uses like this, though they have particular meaning there since they’re used to determine the size of the overall structure. Some of the older classes in JNX have enums that could be INumberEnum
but aren’t yet, but this is just because we haven’t yet gone back to adjust them.