3. Enumerations in Computational Languages

Defining enumerations is directly supported in information modeling languages (such as in UML Class Diagrams), in data schema languages (such as in XML Schema, but not in SQL), and in many programming languages (such as in C++ and Java, but not in JavaScript).

3.1. Enumerations in SQL

Unfortunately, standard SQL does not support enumerations. Some DBMS, such as MySQL and Postgres, provide their own extensions of SQL column definitions in the CREATE TABLE statement allowing to define enumeration-valued columns.

A MySQL enumeration is specified as a list of enumeration labels with the keyword ENUM within a column definition, like so:

CREATE TABLE people (
    name VARCHAR(40),
    gender ENUM('MALE', 'FEMALE', 'UNDETERMINED')
);

A Postgres enumeration is specified as a special user-defined type that can be used in columns definitions:

CREATE TYPE GenderEL AS ENUM ('MALE', 'FEMALE', 'UNDETERMINED');
CREATE TABLE people (
   name text,
   gender GenderEL
)

3.2. Enumerations in XML Schema

In XML Schema, an enumeration datatype can be defined as a simple type restricting the primitive type xs:string in the following way:

<xs:simpleType name="BookCategoryEL">
  <xs:restriction base="xs:string">
    <xs:enumeration value="NOVEL"/>
    <xs:enumeration value="BIOGRAPHY"/>
    <xs:enumeration value="TEXTBOOK"/>
    <xs:enumeration value="OTHER"/>
  </xs:restriction>
</xs:simpleType>

3.3. Enumerations in JavaScript

We can implement an enumeration in the form of a special JavaScript object definition using the Object.defineProperties method:

var BookCategoryEL = null;
Object.defineProperties( BookCategoryEL, {
  NOVEL: {value: 1, writable: false},
  BIOGRAPHY: {value: 2, writable: false},
  TEXTBOOK: {value: 3, writable: false},
  OTHER: {value: 4, writable: false},
  MAX: {value: 4, writable: false},
  labels: {value:["novel","biography","textbook","other"], 
           writable: false}
});

This definition allows using the enumeration literals BookCategoryEL.NOVEL, BookCategoryEL.BIOGRAPHY etc., standing for the enumeration indexes 1, 2 , 3 and 4, in program statements. Notice how this definition takes care of the requirement that enumeration literals like BookCategoryEL.NOVEL are constants, the value of which cannot be changed during program execution. This is achieved with the help of the property descriptor writable: false in the Object.defineProperties statement.

We can also use a generic approach and define a meta-class Enumeration for creating enumerations in the form of special JS objects:

function Enumeration( enumLabels) {
  var i=0, LBL="";
  this.MAX = enumLabels.length;
  this.labels = enumLabels;
  // generate the enum literals as capitalized keys/properties
  for (i=1; i <= enumLabels.length; i++) {
    LBL = enumLabels[i-1].toUpperCase();
    this[LBL] = i;
  }
  // prevent any runtime change to the enumeration
  Object.freeze( this);
};

Using this Enumeration class allows to define a new enumeration in the following way:

var BookCategoryEL = new Enumeration(["novel","biography","textbook","other"])

Having an enumeration like BookCategoryEL, we can then check if an enumeration attribute like category has an admissible value by testing if its value is not smaller than 1 and not greater than BookCategoryEL.MAX. Also, the label can be retrieved in the following way:

formEl.category.value = BookCategoryEL.labels[this.category - 1];

As an example, we consider the following model class Book with the enumeration attribute category:

function Book( slots) {
  this.isbn = "";     // string
  this.title = "";    // string
  this.category = 0;  // number (BookCategoryEL)
  if (arguments.length > 0) {
    this.setIsbn( slots.isbn); 
    this.setTitle( slots.title); 
    this.setCategory( slots.category);
  }
};

For validating input values for the enumeration attribute category, we can use the following check function:

Book.checkCategory = function (c) {
  if (!c) {
    return new MandatoryValueConstraintViolation(
        "A category must be provided!");
  } else if (!Number.isInteger(c) || c < 1 || 
             c > BookCategoryEL.MAX) {
    return new RangeConstraintViolation(
        "The category must be a positive integer " +
        "not greater than "+ BookCategoryEL.MAX +" !");
  } else {
    return new NoConstraintViolation();
  }
};

Notice how the range constraint defined by the enumeration BookCategoryEL is checked: it is tested if the input value c is a positive integer and if it is not greater than BookCategoryEL.MAX.