4. The Meta-Class Enumeration

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 arguiment 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 arguiment such that the codes are used for the names of the enumeration literals. Consequently, the constructor needs to test if the invocation argument is an array or an object. 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 enum 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 (typeof(enumArg) === "object" && 
             Object.keys( enumArg).length > 0) {
    ... // a code list defined by a map
  } else  {
    throw new OtherConstraintViolation(
        "Invalid Enumeration constructor argument: "+ enumArg);          
  }
  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);
};

Notice that a label like "text book" or "text-book" is converted to the enumeration literal name "TEXT_BOOK". 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)) {
    ...
  } else if (typeof(enumArg) === "object" && 
             Object.keys( enumArg).length > 0) {
    // a code list defined by a map
    if (!Object.keys( enumArg).every( function (code) {
            return (typeof( enumArg[code]) === "string"); })) {
      throw new OtherConstraintViolation(
          "All values of a code list 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 +")"; 
    });
  } else  {
    throw new OtherConstraintViolation(
        "Invalid Enumeration constructor argument: "+ enumArg);          
  }
  ...  // as above
};