# OCL Conversion to Schematron

Conversion from OCL to Schematron is performed on the basis of a ShapeChange-internal syntax representation of OCL expressions. The representation is close to the Concrete Syntax structure described in the OCL 2.2 standard.

Naturally, the syntax representation of OCL is recursive. Therefore the principles of translation from OCL to another language can best be described using a recursive notation. Below we describe, how some particular constructs such as the application of the select() iterator

x→select(t|pred(t))

translate to XPath 1.0, where the translation results of the constituent parts (such as x and pred(t)) are presumed.

For a valid OCL expression x let τ(x) denote the equivalent XPath 1.0 expression. The expression x may contain free variables (explicit or implicit), which need to be treated when computing τ(x). One typical variable is self, which translates to current(). So, τ(self)=current().

Category | OCL syntax | In words | Schematron translation
--- | --- | --- | ---
Variable access self | self | The current object in the context of which the expression shall hold. | current() NOTE: Whenever the current node happens to be identical to current() there is no need to explicitly generate current() for self. Relative path syntax is to be used in these cases.
Iterator variable access | t defined in an iterator used in x(t) | t has to be assigned a current value from the path that leads to x(t). | If t has a realization in the path leading to x:../../.. …​/.. (As many .. as are required to reach the binding context of t). No realization in the path (may be xlink:href): Cannot be translated because there is no unique XPath expression to define this.
Let variable access | t defined in a let construct used in x(t) | t necessarily has a value from the initialize expression. | If t is defined in the outer (current()) context: $id, where id is some unique <let> variable. The let initializer is translated in the current() context and initializes a Schemtron <let> element. Other: The let initializer of the variable t is translated in the current context and substitutes t.
Let expression | let x=y in z(x) | Assignment of expression y to variable x. Result is z(x). | If x and y are defined in the outer (current()) context: τ(z(τ(x))) Additionally, a Schematron <let> is created. Other: τ(z(τ(y))) This means we are substituting the initializers.
Integer or real constants | 123 or 3.1415 |  | same
Boolean constants | true or false |  | true() or false()
String constants | 'xxxxx' |  | same
Enumeration constants | Type::value |  | 'value'
Codelist constants | Type::value |  | GML 3.3 rules: The constant is translated to an external codelist reference according to a pattern in tagged values in the codelist class. Other GML version rules: 'value'
If expression | if x then y else z endif | If x evaluates to true then the value of the expression is y, otherwise z. | If τ(y) and τ(z) are represented by nodesets: τ(y)[τ(x)] | τ(z)[not(τ(x))] x needs to be compiled in the tail context of τ(y) and τ(z). If τ(y) and τ(z) are strings: concat(substring(τ(y),number(not(τ(x)))*string-length(τ(y))+1),substring(τ(z), number(τ(x))*string-length(τ(z))+1)) The trick is to concatenate substrings which either comprise the full argument or nothing, depending on the value of the predicate. If τ(y) and τ(z) are numbers or Booleans: As for strings. The result has to be converted into the proper type.
Attribute call | x . attname | Set of object instances reached from the instance or set represented by x by applying attribute attname. | If simple-typed (19136 encoding): τ(x)/attname If simple-typed (19139 encoding – non-codelist): τ(x)/attname/* If simple-typed (19139 encoding – codelist): τ(x)/attname/*/@codeListValue If nested and complex-typed: τ(x)/attname/* If realized by means of xlink:href: *[concat(α,@gml:id,β)=τ(x)/attname/@xlink:href] where α and β are constant prefixes and postfixes surrounding the identifier proper in the xlink:href value. The values for α and β can be configured. If the type of linkage is unknown: A nodeset union of the expressions above.
Attribute call according to nilReason implementation pattern | x . attname . valuex . attname . reason | Set of instances reached by attname, respectively by attname/@nilReason | Case x . attname . value: τ(x.attname) Compilation as above – 'x.attname' is assumed to have the type of 'value'. Case x . attname . reason (19136 encoding): τ(x.attname)[@xsi.nil='true']/@nilReason Case x . attname . reason (19139 encoding): τ(x.attname)[not(*)]/@gco:nilReason
Operation call allInstances() | x . allInstances() | Set of all object instances of type x.x represents a type-valued expression. | If x is a type constant: Nodeset union (n1|…|ni), where nk=//Tk[@gml:id] and Tk is one of the concrete derivations of the type of x (including x). If x is a type expression: Cannot be translated because required schema information is not available at run-time.
Operation call oclIsKindOf() | x . oclIsKindOf(y) | The single object instance x is checked for complying with type y. | If y is a type constant: boolean(τ(x)[name()='T1' or … or name()='Ti']), where Tk is one of the names of the concrete derivations of y, including y. boolean(…) may be omitted if the argument is known to be used by operands, which do an implicit conversion to Boolean. If y is a type expression: Cannot be translated because required schema information is not available at run-time.
Operation call oclIsTypeOf() | x . oclIsTypeOf(y) | The single object instance x is checked for being of type y. | If y is a type constant: boolean(τ(x) [name()='T']), where T is the name of the type y. If y is a type expression: boolean(τ(x)/self::*[name()=name(τ(y))]) boolean(…) may be omitted if the argument is known to be used by operands, which do an implicit conversion to Boolean.
Note | Expression part not implemented.
Type-comparing CharacterString to code lists: We are making an exception to the strict rules with simple data elements which we permit being successfully type-compared to code lists.
Operation call oclAsType() | x . oclAsType(y) | The single object instance x is downcast to type y. The value is 'undefined' if this is not possible. | If y is a type constant: τ(x)[name()='T1' or … or name()='Ti'], where Tk is one of the names of the concrete derivations of y, including y. If y is a type expression: Cannot be translated because required schema information is not available at run-time. Casting CharacterString to code lists: We are making an exception to the strict rules with simple data elements which we permit being casted to code list types.
Operation call +,-,*,/ | x + y, etc. | Value of x.+(y), etc. | τ(x) + τ(y) τ(x) - τ(y) τ(x) * τ(y) τ(x) div τ(y)
Operation calls =, <> | x = y, x <> y | Value of x.=(y), x.<>(y) | If x and y are simple types: τ(x) = τ(y) τ(x) != τ(y) If x and y is are objects: generate-id(τ(x)) = generate-id(τ(y)) generate-id(τ(x)) != generate-id(τ(y))
Operation call <, >, ⇐, >= | x < y | Value of x.<(y), etc. | τ(x) < τ(y) τ(x) > τ(y) τ(x) ⇐ τ(y) τ(x) >= τ(y)
Operation call size() | x . size() | Number of characters in the string instance x. | string-length(τ(x))
Operation call concat() | x . concat(y) | String concatenation of x and y. | concat(τ(x),τ(y)) A series of concats may be joined to a multi-argument concat invocation.
Operation call substring() | x . substring(y,z) | Substring of x running from position y to position z | substring(τ(x), τ(y), τ(z)-τ(y)+1)
Operation call and, or, xor, implies | x and y x or y x xor y x implies y | Logical combination as indicated | τ(x) and τ(y) τ(x) or τ(y) boolean(τ(x))!=boolean(τ(y)) not(τ(x)) or τ(y)
Set operation call size() | x → size() | Number of objects in x. | count(τ(x))
Set operation callisEmpty() | x→isEmpty() | Predicate: Is the set represented by x empty? | not(τ(x))
Set operation callnotEmpty() | x→notEmpty() | Predicate: Is the set represented by x not empty? | boolean(τ(x)) boolean may be omitted, if τ(x) is known to be Boolean or is used by operands, which do an implicit conversion to Boolean.
Iterator call exists() | x → exists(t|b(t)) | Predicate: Does the set x contain an objects t for which the Boolean expression b(t) holds? | boolean(τ(x)[τ(b(.))]) boolean may be omitted, if τ(x) is known to be Boolean or is used by operands, which do an implicit conversion to Boolean.
Iterator call forAll() | x → forAll(t|b(t)) | Predicate: Does the set x only contain objects t for which the Boolean expression b(t) holds? | count(τ(x))=count(τ(x)[τ(b(.))]) In the implementation we map forAll() to exists(). We can do this because according to first level logic, we have:x→forAll(t|b(t)) = not(x→exists(t|not(b(t)))
Iterator call isUnique() | x → isUnique(t|y(t)) | Predicate: Does the set x only contain objects t for which the expression y(t) creates mutually different objects? | This is a hard one, which could only be solved in a few cases: If y is a constant, y(t)=const: count(τ(x))<=1 If y is identity and x is object-valued, y(t)=t: true() This is because nodesets are sets. If y is identity and x is a collection of basic types, y(t)=t: not(τ(x)[.=(preceding::*|ancestor::*)[count(.|τ(x))=count(τ(x))]]) This means any value in τ(x) must not be contained in the intersection of τ(x) with the previous part of the tree. If y is an object-valued attribute, y(t)=t.a: count(τ(x))=count(τ(x.a)) This is true due to the pigeonhole principle. Note that t.a is required to be a single value, not a set! If y is an attribute carrying a basic data type, y(t)=t.b (19136 encoding): not(τ(x)[b=(preceding::*|ancestor::*)[count(.|τ(x))=count(τ(x))]/b]) This means the value of any b must not be contained in the intersection of τ(x) with the previous part of the tree. As above, t.b needs to be a single value. If y is an attribute carrying a basic data type, y(t)=t.b (19139 encoding): not(τ(x)[b/*=(preceding::*|ancestor::*)[count(.|τ(x))=count(τ(x))]/b/*])
Note | This is again different for 19139 codelist access. See "attribute call" row for this.
Nested attributes of either kind, y(t)=t.a1.a2…b: Each single step needs to be unique. Hence we can reduce this to: τ(x→isUnique(t|t.a1)) and τ(x.a1→isUnique(t|t.a2)) and … and τ(x.a1.a2…→isUnique(t|t.b)) Any other, particularly arbitrary expressions: Cannot be translated because no way to express this in XPath 1.0 has been found.
Iterator call select() | x → select(t|b(t)) | Compute the set of those objects t in x, for which the predicate b(t) holds. | τ(x) [τ(b(.))] Note that this is very similar to exists(), the only difference being the Boolean interpretation of the result in the exists() case.
Pattern matching function on Strings | x . matches( pattern ) Note: This operation call is an extension. It is not part of the OCL standard. | Boolean function which yields true if the pattern of type String matches the String argument. | There is no way to express matches() in XPath 1.0 except by way of using a Java extension function or by making use of the matches function available in XPath 2.0. The implementation allows configuring either the use of an extension function or of XPath 2.0 syntax. The XPath translation target is configurable text (a function call), which receives τ(x) and τ(pattern) as substitutes for the strings '$object$' and '$pattern$', which both have to be part of the configured function call.