Semantic shift occurs when a data value of one data type is converted into a data value of another data type. All such conversions require a semantic shift mapping. OOA09 specifies a set of predefined semantic shift mappings which can be used within a domain or between two different domains. OOA09 also allows counterpart mappings and user-defined semantic shift mappings to be defined on a bridge. All semantic shift mappings are implicitly invoked during Action Language assignment or parameter passing. Counterpart mappings and user-defined semantic shift mappings can only be implicitly invoked within bridge mappings and domain observers since they always involve data types from different domains. Although there is no Action Language cast operator, a declare statement can be used to perform an explicit semantic shift, e.g.
declare n as Real;
declare integer_n as Integer = n;
There are many situations where a semantic shift mapping can result in an error. If a user wants to handle the error within their Action Language code then the semantic shift must be performed between a mandatory attribute or data item and a conditional attribute or data item, e.g.
declare text as String = "Hello World";
declare unsafe_text as empty or one String = text;
// The following generates a run-time error!
declare unsafe_n as Integer = text;
// The following is entirely safe
// as long as the necessary checks are performed
declare safe_n as empty or one Integer = text;
if (empty safe_n)
// Handle syntax or out of range error...
end if;
// The following generates a run-time error!
declare unsafe_m as empty or one Integer = unsafe_text;
// The following is entirely safe
// as long as the necessary checks are performed
if (empty unsafe_text)
// Handle missing text...
end if;
declare safe_text as String = unsafe_text;
declare safe_m as empty or one Integer = safe_text;
if (empty safe_m)
// Handle syntax or out of range error...
end if;
In addition to type specific errors, a semantic shift between a conditional attribute or data item and a mandatory attribute or data item always generates a run-time error when the source value is empty, i.e. a
NullPointerException
in Java.
It is important to note that bridges do not define their own data types. All data types referenced within a bridge mapping are from either the client or server domain. All data types referenced within a domain observer are from one of the domains defined within the domain observer's enclosing project. It is also worth noting that the only data stored in a bridge is cached counterpart mappings. The Shlaer-Mellor concept of semantic shift was first discussed in [Wormhole96].
The Executable UML core types (Boolean
, String
, Integer
, Real
, Date
, Timestamp
and Arbitrary_ID
) which are all predefined types in OOA09 are not global types. However, there are predefined semantic shift mappings between the same core types in all domains, i.e. core types from all domains can be used interchangeably.
There are predefined semantic shift mappings between all boolean types since all boolean values have the same semantics or meaning in OOA09 even if they have different aliases. There is also a predefined mapping to String
which converts a boolean value to the boolean type's notation independent alias for that value, e.g. Boolean
true value maps to "True"
(not Shlaer-Mellor "TRUE"
or Executable UML "true"
). The reverse String
to boolean type mapping is also predefined where aliases or the hard-coded "True"
or "False"
names are fuzzy matched according to standard name comparison conventions (see Naming).
There are no predefined semantic shift mappings between any enumerated types since each legal value has its own unique meaning. A user-defined semantic shift mapping (which should always use a switch
statement for safety) must be defined whenever any such conversion is required. However, there is a predefined mapping to String
which converts an enumerated value to the enumerated type's notation independent name for that value. The reverse String
to enumerated type mapping is also predefined where legal value names are fuzzy matched according to standard name comparison conventions.
There are no predefined semantic shift mappings between symbolic types in general since each symbolic type is assumed to have unspecified but definite semantics. The only exception involves the core type String
which has no syntax, semantics or length restrictions. All symbolic types have a predefined semantic shift mapping to String
. The reverse String
to symbolic type mapping is also predefined. However, an error will occur if a target pattern is not matched or text length is invalid.
There are no predefined semantic shift mappings between numeric types in general since each numeric type is assumed to have unspecified but definite semantics. However, there are predefined semantic shift mappings between all numeric types and several core types including String
, Integer
and Real
since core types are assumed to have no domain specific semantics. Additionally, there are predefined semantic shift mappings between all numeric types with a Time
value format and the core types Date
and Timestamp
. There are also predefined semantic shift mappings going in the reverse direction in both cases. For all numeric values, truncation of decimal places may occur during a semantic shift. For time values, scaling up or down of time units may also occur during a semantic shift. However, one should note that time values in OOA09 are always stored as universal time (i.e. GMT) values relative to the Java Date Epoch (1 January 1970).
There are predefined semantic shift mappings between all arbitrary ID types and several core types including String
and Integer
. There are also predefined semantic shift mappings going in the reverse direction. These are in addition to the predefined semantic shift mappings between Arbitrary_ID
core types in all domains. However, great caution should be used when passing arbitrary ID values around since arbitrary ID attributes may have their values reallocated automatically whenever object instances are created or deleted or whenever related object instances are assigned new arbitrary ID values.
There are no predefined semantic shift mappings between object instance types. However, a counterpart mapping can be defined between an object instance in one domain and an object instance in another domain. A counterpart mapping operation is invoked whenever a specific object instance in the client domain is shifted for the first time. The operation is expected to return the counterpart object instance in the target domain. The operation may return nothing if the counterpart mapping is conditional. However, the operation will not be invoked again in the future. Once a counterpart mapping has been established between two object instances then the link remains in a bridge cache until either of the object instances is deleted. This is not a flexible mapping for dynamic counterpart relationships. It is equivalent to joining two classes in different packages using multiple inheritance. It creates a permanent link which is only broken when one part is deleted. Although counterpart mappings are one-way, a counterpart mapping and it's reverse mapping share the same bridge cache. Once a link has been formed in either direction, a semantic shift can be performed in both directions as long as counterpart mappings were defined in both directions. Dynamic counterpart relationships can still be created using user-defined semantic shift mappings between object instance types. However, such mappings are always re-invoked when a semantic shift is requested, i.e. counterpart mappings will always be much faster.
There are no general purpose predefined semantic shift mappings involving event instance, return coordinate or transfer vector types. However, an event instance type for a solicit event parameter of a request wormhole has a predefined mapping to a transfer vector type for an event data item or input parameter of a control reception point (see Asynchronous Communications between Domains). In the opposite direction, a transfer vector type for a self parameter of an asynchronous return wormhole has a predefined mapping to an event instance type for a self parameter of an asynchronous return mapping.
There are predefined semantic shift mappings between all data types and all external types (in another domain) since converting an arbitrary data value into an external value only involves wrapping the original data value as an external value. No information is lost in the process. However, the reverse mapping is not so straightforward. If an external type has only been mapped from a single data type (across all bridges) then the reverse mapping is entirely safe. Otherwise, the reverse mapping is only safe if the wrapped data value has exactly the same type as the target type. An error is generated if the actual type does not match the target type.
User-defined semantic shift mappings can only be defined in a bridge between client and server data types. An analyst should not need to define a mapping between two data types within the same domain. User-defined semantic shift mappings between boolean types should never be defined since boolean value semantics are fixed across all domains. User-defined semantic shift mappings involving core types should also never be defined since core value semantics are also fixed across all domains. A user-defined semantic shift mapping between a pair of object instance types should not be defined if a counterpart mapping for that pair is already defined. User-defined semantic shift mappings should also not involve return coordinate or transfer vector types. Finally, user-defined semantic shift mappings must never involve external types since the predefined semantic shift mappings involving external types always take precedence. Given these restrictions and rules, user-defined semantic shift mappings should never overlap any of the predefined semantic shift mappings.
Semantic shift mappings are summarized in the following table:
Source Type | Target Type | Semantic Shift Mapping |
Boolean Type | Boolean Type | predefined |
Boolean Type | String | predefined |
String | Boolean Type | predefined (error if boolean value not fuzzy matched) |
Enumerated Type | String | predefined |
String | Enumerated Type | predefined (error if legal value not fuzzy matched) |
Symbolic Type | String | predefined |
String | Symbolic Type | predefined (error if pattern not matched or length invalid) |
Numeric Type | String | predefined |
Numeric Type | Integer | predefined (truncate decimal places) |
Numeric Type | Real | predefined |
Numeric Type (with Time value format) | Date | predefined (scale up or down to match time units, truncate decimal places) |
Numeric Type (with Time value format) | Timestamp | predefined (scale up to match time units, truncate decimal places) |
String | Numeric Type | predefined (error if not real or out of range, truncate to maximum decimal places) |
Integer | Numeric Type | predefined (error if out of range) |
Real | Numeric Type | predefined (error if out of range, truncate to maximum decimal places) |
Date | Numeric Type (with Time value format) | predefined (error if out of range, scale up or down to match time units) |
Timestamp | Numeric Type (with Time value format) | predefined (error if out of range, scale down to match time units) |
Arbitrary ID Type | String | predefined |
Arbitrary ID Type | Integer | predefined |
String | Arbitrary ID Type | predefined (error if not integer or out of range) |
Integer | Arbitrary ID Type | predefined (error if out of range) |
Arbitrary_ID | Arbitrary_ID | predefined |
Object Instance Type | Object Instance Type | counterpart mapping (error if no counterpart and mapping not conditional) |
Event Instance Type (for solicit event parameter of request wormhole) | Transfer Vector Type (for event data item or input parameter of control reception point) | predefined |
Transfer Vector Type (for self parameter of asynchronous return wormhole) | Event Instance Type (for self parameter of asynchronous return mapping) | predefined |
Data Type | External Type | predefined |
External Type | Data Type | predefined (error if actual type not target type) |
Data Type | Data Type | user-defined (error if no return value and mapping not conditional) |