We define an Enumeration
meta-class, which supports
both simple enumerations and code lists (but not record enumerations).
While a simple enumeration is defined by a list of labels in the form of a
JS array as the constructor argument such that the labels are used for the
names of the enumeration literals, a code list is defined as a special
kind of key-value map in the form of a JS object as the constructor
argument such that the codes are used for the names of the enumeration
literals. Consequently, the constructor needs to test if the invocation
argument is a JS array or not. The following first part of the code shows
how simple enumerations are created:
function Enumeration( enumArg) { var i=0, lbl="", LBL=""; if (Array.isArray( enumArg)) { // a simple enumeration defined by a list of labels if (!enumArg.every( function (n) { return (typeof n === "string"); })) { throw new OtherConstraintViolation( "A list of enumeration labels must be an array of strings!"); } this.labels = enumArg; this.enumLitNames = this.labels; this.codeList = null; } else if (...) { ... // a code list defined by a code/label map } this.MAX = this.enumLitNames.length; // generate the enumeration literals by capitalizing/normalizing for (i=1; i <= this.enumLitNames.length; i++) { // replace " " and "-" with "_" lbl = this.enumLitNames[i-1].replace(/( |-)/g, "_"); // convert to array of words, capitalize them, and re-convert LBL = lbl.split("_").map( function (lblPart) { return lblPart.toUpperCase(); }).join("_"); // assign enumeration index this[LBL] = i; } Object.freeze( this); };
After setting the MAX
property of the newly created
enumeration, the enumeration literals are created in a loop as further
properties of the newly created enumeration such that the property name is
the normalized label string and the value is the index, or sequence
number, starting with 1. Notice that a label string like "text book" or
"text-book" is normalized to the enumeration literal name "TEXT_BOOK",
following a widely used convention for constant names. Finally, by
invoking Object.freeze
on the newly created enumeration, all
its properties become 'unwritable' (or read-only).
The following second part of the code shows how code list enumerations are created:
function Enumeration( enumArg) { var i=0, lbl="", LBL=""; if (Array.isArray( enumArg)) { // a simple enumeration ... } else if (typeof enumArg === "object" && Object.keys( enumArg).length > 0) { // a code list defined by a code/label map if (!Object.keys( enumArg).every( function (code) { return (typeof( enumArg[code]) === "string"); })) { throw new OtherConstraintViolation( "All values of a code/label map must be strings!"); } this.codeList = enumArg; // use the codes as the names of enumeration literals this.enumLitNames = Object.keys( this.codeList); this.labels = this.enumLitNames.map( function (c) { return enumArg[c] +" ("+ c +")"; }); } ... };
Notice that the code list labels in this.labels
are
extended by appending their codes in parenthesis.