# Data Model Documentation

These HTML pages are available in book form as a postscript file and as a. PDF file.

#### Overview of the Data Model

The ABLE data model provides a common set of literal and variable data objects that are used in other ABLE components, such as filters, rulesets, and inference engines, and can be extended by others for private needs.

Literals are immutable objects and are assigned a value at construction time. Variables can be assigned values at any time, either from other variables or from literals. A set of operators allows comparing literals and variables and assigning new values to variables.

All literals and variables are typed, and fall into one of several categories:

1. Boolean - values are either true or false.
2. Byte - values are integer numbers, which are represented internally as Java bytes.
3. Character - values are single-quoted characters or control values.
4. Double - values are real numbers, which are represented internally as Java doubles.
5. Float - values are real numbers, which are represented internally as Java floats.
6. Integer - values are integer numbers, which are represented internally as Java ints.
7. Long - values are integer numbers, which are represented internally as Java longs.
8. Short - values are integer numbers, which are represented internally as Java shorts.
9. String - values are Java Strings.
10. Object - values are any Java Object.

When assigning or comparing values, some attempt is made at converting one data type to another. For example, a Double variable may be assigned a value from a Boolean variable (or literal) since a boolean data object, whose value is either true or false, is considered to have a numeric value of either 1.0 or 0.0, respectively. However, not all combinations of data assignment and comparison are possible, and AbleDataExceptions are thrown when appropriate. For example, the less than operator has no meaning for generic data types whose "real" Java data types are not numbers or strings at the time an assignment or comparison is made.

In adition to the data types mentioned above, there is another data type, called a call literal. Like other literals, it is a read-only object, but one that wraps an ABLE user-defined function or method call. This means that every time a call literal's current value is requested, the data that is returned is actually the data obtained by calling the wrappered function. The returned data can be any Java Object, but must be, of course, a type expected by the receiving application.

All literals and variables implement, at minimum, the following methods found in the AbleRd interface, which is the interface that must be implemented by all "readable" data objects:

      public boolean      getBooleanValue()
      public AbleFuzzySet getFuzzyValue()
      public Object       getGenericValue()
      public double       getNumericValue()
      public String       getStringValue()
      public AbleLiteral  getValue()

AbleLiteral is a partial implementation of the AbleRd interface; AbleBooleanLiteral, AbleCallLiteral, AbleFuzzySet, AbleGenericLiteral, AbleDoubleLiteral, and AbleStringLiteral are the concrete implementations.

Variables implement, at minimum, the additional following methods found in the AbleWr interface, which is the interface that must be implemented by all "writable" data objects:

      public void setBooleanValue(boolean)
      public void setFuzzyValue(AbleFuzzySet)
      public void setGenericValue(Object)
      public void setNumericValue(double)
      public void setStringValue(String)
      public void setValue(AbleLiteral)

AbleVariable is a partial implementation of the AbleWr interface; AbleBooleanVariable, AbleCategoricalVariable, AbleContinuousVariable, AbleDiscreteVariable, AbleFuzzyVariable, AbleGenericVariable, AbleDoubleVariable, and AbleStringVariable are the concrete implementations.

Given the "get" and "set" methods listed above, the following coding is possible:

      double  aDbl  = someNumericVar.getNumericValue();
      boolean aBool = someNumericVar.getBooleanValue();
      String  aStr  = someNumericVar.getStringValue();

      anotherNumericVar.setNumericValue(aDbl);
      anotherNumericVar.setNumericValue(someNumericVar.getNumericValue());
      anotherNumericVar.setStringValue(aStr);

      anotherNumericVar.setValue(someNumericVar.getValue());

      anyTypeOfVar.setValue(anyOtherVar.getValue());
      anyTypeOfVar.setValue(anyTypeOfLiteral);

      if (someNumericVar.getNumericValue() <= anotherNumericVar.getNumericValue())

However, all of the assignment and comparison operations above can be better accomplished using the built-in "operators" defined in the AbleRd and AbleWr classes. AbleRd, the interface that defines "readable" data objects, defines these comparison operators, which are implemented in all AbleLiterals and AbleVariables:

      public boolean cmpEq  (AbleRd) // ==
      public boolean cmpGt  (AbleRd) // >
      public boolean cmpGtEq(AbleRd) // >=
      public double  cmpIs  (AbleRd) // is, fuzzy compare
      public boolean cmpLt  (AbleRd) // <
      public boolean cmpLtEq(AbleRd) // <=
      public boolean cmpNeq (AbleRd) // !=

AbleWr, the interface that defines "writable" data objects, defines these assignment operators, which are implemented in all AbleVariables:

      public void asgnEq(AbleRd)         // boolean assignment
      public void asgnIs(AbleRd)         // fuzzy assignment
      public void asgnIs(AbleRd, double) // fuzzy asignment with correlation

Given the above comparison and assignment operators, the following style of coding is possible:

      if ( someNumericVar.cmpLtEq(anotherNumericVar) ) {
        someStringVar.asgnEq(anotherStringVar);
      }

...and so on. In summary, these are the comparison operators implemented by all AbleLiterals and AbleVariables:

Method | Function | Usage
--- | --- | ---
cmpEq | Boolean compare, equal to (==) | AbleRd.cmpEq(AbleRd)
cmpGt | Boolean compare, greater than (>) | AbleRd.cmpGt(AbleRd)
cmpGtEq | Boolean compare, greater than or equal to (>=) | AbleRd.cmpGtEq(AbleRd)
cmpIs | Fuzzy compare (is) | AbleFuzzyVariable.cmpIs(AbleFuzzySet)
cmpLt | Boolean compare, less than (<) | AbleRd.cmpLt(AbleRd)
cmpLtEq | Boolean compare, less than or equal to (<=) | AbleRd.cmpLtEq(AbleRd)
cmpNeq | Boolean compare, not equal to (!=) | AbleRd.cmpNeq(AbleRd)

And these are the assignment operators implemented by all AbleVariables:

Method | Function | Usage
--- | --- | ---
asgnEq | Boolean assignment (=) | AbleVariable.asgnEq(AbleRd)
asgnIs | Fuzzy assignment (is) | AbleFuzzyVariable.asgnEq(AbleFuzzySet)

The remainder of this paper contains further details about all literals and variables in the Able data package.

#### Class Hierarchy

Here are the classes that make up the Able Data Model:

Remember that all literals and variables are classifed into basic types:

1. Boolean
2. Byte
3. Character
4. Double
5. Float
6. Integer
7. Long
8. Short
9. String
10. Object

and lastly,

11. CallLiteral

Essentially, any data object can be compared to any other data object, and any data object and be assigned to any variable. This is accomplished through the various `get...Value()` and `set...Value()` methods. The following sections list the data objects in each category (boolean, double, string, and generic) and contain a chart showing what each `get...Value()` method returns. Each method's return value depends, of course, on the data object's actual current "raw" value at the time the method is invoked.

Looking at the chart for boolean data types, for example, you can see that when a boolean variable's current value is true, `getBooleanValue()` returns true, `getNumericValue()` returns 1.0, and `getStringValue()` returns "True", but when the current value is false `getBooleanValue()` returns false, `getNumericValue()` returns 0.0, and `getStringValue()` returns "False". Thus it is possible to assign a numeric variable a value from a boolean literal or variable, and so on.

When a particular combination is not possible, or when the current raw value of a particular data object doesn't lend itself to conversion to another data type, an AbleDataException is thrown. This may happen, for example, when trying to assign a numeric variable a value from a string literal or string variable. If the source string data object contains something like "123.456" or "-0.005", the assignment is possible, because the string can be parsed into a number. But if the string data object contains something like "Kilroy was here", an AbleDataException is thrown.

#### Boolean Data Objects

There are two boolean data objects. They are:

- AbleBooleanLiteral
- AbleBooleanVariable

  Both the literal and the variable must be given an initial value of either true or false when created.

For convenience, there are two pre-defined, static AbleBooleanLiterals: AbleData.True and AbleData.False.

Return values from boolean data object get...Value() methods
Boolean object's current value | getBooleanValue() | getGenericValue() | getNumericValue() | getStringValue() | getValue()
--- | --- | --- | --- | --- | ---
true | true | new Boolean(true) | 1.0 | "True" | new AbleBooleanLiteral(true)
false | false | new Boolean(false) | 0.0 | "False" | new AbleBooleanLiteral(false)

#### Numeric Data Objects

The numeric data objects are:

- AbleContinuousVariable

  Continuous variables are numeric variables whose current value must be between a minimum and a maximum, inclusive, both of which are specified when the variable is created. Any attempt to set a continuous variable to a value outside the specified range results in an AbleDataException.

  For historical reasons, continuous variables have an initial value of Double.NaN.

- AbleDiscreteVariable

  Discrete variables are numeric variables whose current value must be a value chosen from a finite list of numeric values. The list of acceptable values can be specified when a discrete variable is created, or it can be set for the variable at anytime. Methods are also provided to add and remove values from the list of acceptable values. However, changing a variable's acceptable value list after a variable is created and used can have ramifications for the variable's current value. Any attempt to set a discrete variable to a value not in the acceptable value list results in an AbleDataException.

  For historical reasons, discrete variables have an initial value of Double.NaN.

- AbleFuzzyVariable
- AbleFuzzySet

  Fuzzy variables are numeric variables whose current value is a fuzzy number represented by a fuzzy set. Fuzzy variables can also have a crisp value, which is the defuzzifed fuzzy number. Fuzzy sets represent fuzzy numbers and are AbleLiterals. Although not shown in the chart below, fuzzy variables support get- and setFuzzyValue() and fuzzy sets support getFuzzyValue().

- AbleDoubleLiteral
- AbleDoubleVariable

  Both the literal and the variable must be given an initial value when created. The value can be any number as values are completely unrestricted.

Return values from numeric data object get...Value() methods
Numeric object's current value | getBooleanValue() | getGenericValue() | getNumericValue() | getStringValue() | getValue()
--- | --- | --- | --- | --- | ---
0.0 | false | new Double(0.0) | 0.0 | "0.0" | new AbleNumericLiteral(0.0)
any non-zero number, n.m | true | new Double(n.m) | n.m | "n.m" | new AbleNumericLiteral(n.m)

#### String Data Objects

The string data objects are:

- AbleCategoricalVariable

  Categorical variables are string variables whose current value must be a value chosen from a finite list of strings. The list of acceptable strings can be specified when a categorical variable is created, or it can be set for the variable at anytime. Methods are also provided to add and remove strings from the list of acceptable strings. However, changing a variable's acceptable value list after a variable is created and used can have ramifications for the variable's current value. Any attempt to set a categorical variable to a string not in the acceptable string list results in an AbleDataException.

  For historical reasons, categorical variables have an initial value of AbleData.StringNull ("Able_NULL_Able").

- AbleStringLiteral
- AbleStringVariable

  Both the literal and the variable must be given an initial value when created. The value can be any string as values are completely unrestricted.

Return values from string data object get...Value() methods
String object's current value | getBooleanValue() | getGenericValue() | getNumericValue() | getStringValue() | getValue()
--- | --- | --- | --- | --- | ---
"true" (case insensitive) | true | new String("true") | AbleDataException | "true" | new AbleStringLiteral("true")
"false" (case insensitive) | false | new String("false") | AbleDataException | "false" | new AbleStringLiteral("false")
string form of a parsable number, "n.m" | AbleDataException | new String("n.m") | (Double.valueOf("n.m")).doubleValue() | "n.m" | new AbleStringLiteral("n.m")
any other string, "foo" | AbleDataException | new String("foo") | AbleDataException | "foo" | new AbleStringLiteral("foo")

Behavior of string data object set...Value() methods
Source object's current value | setBooleanValue() | setGenericValue() | setNumericValue() | setStringValue() | setValue()
--- | --- | --- | --- | --- | ---
boolean true | setStringValue("true") | N/A
boolean false | setStringValue("false") | N/A
Boolean, b | N/A | setBooleanValue(b.booleanValue()) | N/A
Number, n | N/A | setNumericValue(n.doubleValue()) | N/A
String, s | N/A | setStringValue(s) | N/A
AbleLiteral, a | N/A | setValue(a) | N/A
Object, o | N/A | AbleDataException | N/A
any double, n.m | N/A | setStringValue(Double.toString(n.m)) | N/A
any String, "foo" | N/A | "foo" For categorical variables, "foo" must be in the list of acceptable strings. | N/A
any AbleLiteral, a | N/A | calls set...Value(a.get...Value()) depending on type of literal

#### Generic Data Objects

The generic data objects are:

- AbleGenericLiteral
- AbleGenericVariable

  Both the literal and the variable must be given an initial value when created. The value can be any Java Object as values are completely unrestricted.

Return values from generic data object get...Value() methods
Generic object's current value | getBooleanValue() | getGenericValue() | getNumericValue() | getStringValue() | getValue()
--- | --- | --- | --- | --- | ---
Boolean, b | b.booleanValue() | b | 1.0 if true; 0.0, if false | "True" if true; "False" if false | new AbleGenericLiteral(b)
Number, n | false if n.doubleValue()==0.0; true otherwise | n | n.doubleValue() | Double.toString(n.doubleValue()) | new AbleGenericLiteral(n)
String, s | true if s.equalsIgnoreCase("true"); false if s.equalsIgnoreCase("false"); otherwise AbleDataException | s | (Double.valueOf(s)).doubleValue() or AbleDataException | s | new AbleGenericLiteral(s)
AbleLiteral, a | a.getBooleanValue() | a | a.getNumericValue() | a.getStringValue() | new AbleGenericLiteral(a)
any other Object, o | AbleDataException | o | AbleDataException | o.toString() | new AbleGenericLiteral(o)

#### CallLiteral Data Objects

The call literal objects are:

- AbleCallLiteral

A call literal is a wrapper for an Able user-defined function. This means that every time a call literal's current value is requested, the data that is returned is actually the data obtained by immediately and synchronously calling the wrappered function. Call literals more or less expect that the data returned by the wrappered function is a Boolean, Number, or String, but in reality, any Java Object is allowed. The returned data must be, of course, a type expected by the receiving application, which will most likely use the `getGenericValue()` method to obtain the call literal's value.

While call literals may appear on both the left-hand and right-hand side of comparison expressions, they may appear only on the right-hand side of assignment expressions; that is, like any literal, you cannot assign a value to it.

A `get...Value()` chart is not given here, because the code dealing with return values from user-defined functions is too complex to summarize neatly in a table.

#### Extending the Data Objects

While it is not possible to add completely new data types to the Able data model, it is possible to extend any of the existing data objects with additional or modified behavior. If you want to create a ComplexNumberVariable, for example, you might start by extending the AbleNumericVariable class, the AbleContinuousVariable class, or the AbleDiscreteVariable class, depending on whether you want your complex numbers completely unrestricted, limited within a certain range, or limited to one-of-n complex values, respectively. If you choose to extend AbleContinuousVariable so that you can limit your complex numbers to a range, you probably need to override the setDiscourseLo(), setDiscourseHi(), and withinUniversOfDiscourse() methods, among others, to get the desired behavior. You also need to override or perhaps even add new "operator" methods appropriate for your new ComplexNumber data object.

The AbleFuzzyVariable object is an example of a new variable derived from an existing class: it is derived from AbleContinuousVariable.

Last modified: Fri Feb 9 10:52:49 CST 2001