(* Title: Safe OCL
Author : Denis Nikiforov , March 2019
Maintainer : Denis Nikiforov < denis . nikif at gmail . com >
License : LGPL
*)
chapter ‹ Examples›
theory OCL_Examples
imports OCL_Normalization
begin
(*** Classes ****************************************************************)
section ‹ Classes›
datatype classes1 =
Object | Person | Employee | Customer | Project | Task | Sprint
inductive subclass1 where
"c ≠ Object ==>
subclass1 c Object"
| "subclass1 Employee Person"
| "subclass1 Customer Person"
instantiation classes1 :: semilattice_sup
begin
definition "(<) ≡ subclass1"
definition "(≤ ) ≡ subclass1= = "
fun sup_classes1 where
"Object ⊔ _ = Object"
| "Person ⊔ c = (if c = Person ∨ c = Employee ∨ c = Customer
then Person else Object)"
| "Employee ⊔ c = (if c = Employee then Employee else
if c = Person ∨ c = Customer then Person else Object)"
| "Customer ⊔ c = (if c = Customer then Customer else
if c = Person ∨ c = Employee then Person else Object)"
| "Project ⊔ c = (if c = Project then Project else Object)"
| "Task ⊔ c = (if c = Task then Task else Object)"
| "Sprint ⊔ c = (if c = Sprint then Sprint else Object)"
lemma less_le_not_le_classes1:
"c < d ⟷ c ≤ d ∧ ¬ d ≤ c"
for c d :: classes1
unfolding less_classes1_def less_eq_classes1_def
using subclass1.simps by auto
lemma order_refl_classes1:
"c ≤ c"
for c :: classes1
unfolding less_eq_classes1_def by simp
lemma order_trans_classes1:
"c ≤ d ==> d ≤ e ==> c ≤ e"
for c d e :: classes1
unfolding less_eq_classes1_def
using subclass1.simps by auto
lemma antisym_classes1:
"c ≤ d ==> d ≤ c ==> c = d"
for c d :: classes1
unfolding less_eq_classes1_def
using subclass1.simps by auto
lemma sup_ge1_classes1:
"c ≤ c ⊔ d"
for c d :: classes1
by (induct c; auto simp add: less_eq_classes1_def less_classes1_def subclass1.simps)
lemma sup_ge2_classes1:
"d ≤ c ⊔ d"
for c d :: classes1
by (induct c; auto simp add: less_eq_classes1_def less_classes1_def subclass1.simps)
lemma sup_least_classes1:
"c ≤ e ==> d ≤ e ==> c ⊔ d ≤ e"
for c d e :: classes1
by (induct c; induct d;
auto simp add: less_eq_classes1_def less_classes1_def subclass1.simps)
instance
apply intro_classes
apply (simp add: less_le_not_le_classes1)
apply (simp add: order_refl_classes1)
apply (rule order_trans_classes1; auto)
apply (simp add: antisym_classes1)
apply (simp add: sup_ge1_classes1)
apply (simp add: sup_ge2_classes1)
by (simp add: sup_least_classes1)
end
code_pred subclass1 .
fun subclass1_fun where
"subclass1_fun Object C = False"
| "subclass1_fun Person C = (C = Object)"
| "subclass1_fun Employee C = (C = Object ∨ C = Person)"
| "subclass1_fun Customer C = (C = Object ∨ C = Person)"
| "subclass1_fun Project C = (C = Object)"
| "subclass1_fun Task C = (C = Object)"
| "subclass1_fun Sprint C = (C = Object)"
lemma less_classes1_code [code]:
"(<) = subclass1_fun"
proof (intro ext iffI)
fix C D :: "classes1"
show "C < D ==> subclass1_fun C D "
unfolding less_classes1_def
apply (erule subclass1.cases, auto)
using subclass1_fun.elims(3 ) by blast
show "subclass1_fun C D ==> C < D "
by (erule subclass1_fun.elims, auto simp add: less_classes1_def subclass1.intros )
qed
lemma less_eq_classes1_code [code]:
"(≤ ) = (λx y. subclass1_fun x y ∨ x = y)"
unfolding dual_order.order_iff_strict less_classes1_code
by auto
(*** Object Model ***********************************************************)
section ‹ Object Model›
abbreviation "Γ0 ≡ fmempty :: classes1 type env"
declare [[coercion "ObjectType :: classes1 ==> classes1 basic_type" ]]
declare [[coercion "phantom :: String.literal ==> classes1 enum" ]]
instantiation classes1 :: ocl_object_model
begin
definition "classes_classes1 ≡
{|Object, Person, Employee, Customer, Project, Task, Sprint|}"
definition "attributes_classes1 ≡ fmap_of_list [
(Person, fmap_of_list [
(STR ''name'', String[1] :: classes1 type)]),
(Employee, fmap_of_list [
(STR ''name'', String[1]),
(STR ''position'', String[1])]),
(Customer, fmap_of_list [
(STR ''vip'', Boolean[1])]),
(Project, fmap_of_list [
(STR ''name'', String[1]),
(STR ''cost'', Real[?])]),
(Task, fmap_of_list [
(STR ''description'', String[1])])]"
abbreviation "assocs ≡ [
STR ''ProjectManager'' ↦ f [
STR ''projects'' ↦ f (Project, 0::nat, ∞ ::enat, False, True),
STR ''manager'' ↦ f (Employee, 1, 1, False, False)],
STR ''ProjectMember'' ↦ f [
STR ''member_of'' ↦ f (Project, 0, ∞ , False, False),
STR ''members'' ↦ f (Employee, 1, 20, True, True)],
STR ''ManagerEmployee'' ↦ f [
STR ''line_manager'' ↦ f (Employee, 0, 1, False, False),
STR ''project_manager'' ↦ f (Employee, 0, ∞ , False, False),
STR ''employees'' ↦ f (Employee, 3, 7, False, False)],
STR ''ProjectCustomer'' ↦ f [
STR ''projects'' ↦ f (Project, 0, ∞ , False, True),
STR ''customer'' ↦ f (Customer, 1, 1, False, False)],
STR ''ProjectTask'' ↦ f [
STR ''project'' ↦ f (Project, 1, 1, False, False),
STR ''tasks'' ↦ f (Task, 0, ∞ , True, True)],
STR ''SprintTaskAssignee'' ↦ f [
STR ''sprint'' ↦ f (Sprint, 0, 10, False, True),
STR ''tasks'' ↦ f (Task, 0, 5, False, True),
STR ''assignee'' ↦ f (Employee, 0, 1, False, False)]]"
definition "associations_classes1 ≡ assocs"
definition "association_classes_classes1 ≡ fmempty :: classes1 ⇀ f assoc"
text ‹
begin{verbatim}
Project
: membersCount() : Integer[1] = members->size()
: membersByName(mn : String[1]) : Set(Employee[1]) =
members->select(member | member.name = mn)
def: allProjects() : Set(Project[1]) =
Project[1].allInstances()
end{verbatim}›
definition "operations_classes1 ≡ [
(STR ''membersCount'', Project[1], [], Integer[1], False,
Some (OperationCall
(AssociationEndCall (Var STR ''self'') DotCall None STR ''members'')
ArrowCall CollectionSizeOp [])),
(STR ''membersByName'', Project[1], [(STR ''mn'', String[1], In)],
Set Employee[1], False,
Some (SelectIteratorCall
(AssociationEndCall (Var STR ''self'') DotCall None STR ''members'')
ArrowCall [STR ''member''] None
(OperationCall
(AttributeCall (Var STR ''member'') DotCall STR ''name'')
DotCall EqualOp [Var STR ''mn'']))),
(STR ''allProjects'', Project[1], [], Set Project[1], True,
Some (MetaOperationCall Project[1] AllInstancesOp))
] :: (classes1 type, classes1 expr) oper_spec list"
definition "literals_classes1 ≡ fmap_of_list [
(STR ''E1'' :: classes1 enum, {|STR ''A'', STR ''B''|}),
(STR ''E2'', {|STR ''C'', STR ''D'', STR ''E''|})]"
lemma assoc_end_min_less_eq_max:
"assoc |∈ | fmdom assocs ==>
fmlookup assocs assoc = Some ends ==>
role |∈ | fmdom ends ==>
fmlookup ends role = Some end ==>
assoc_end_min end ≤ assoc_end_max end"
unfolding assoc_end_min_def assoc_end_max_def
using zero_enat_def one_enat_def numeral_eq_enat by auto
lemma association_ends_unique:
assumes "association_ends' classes assocs C from role end1 "
and "association_ends' classes assocs C from role end2 "
shows "end1 = end2 "
proof -
have "¬ association_ends_not_unique' classes assocs" by eval
with assms show ?thesis
using association_ends_not_unique'.simps by blast
qed
instance
apply standard
unfolding associations_classes1_def
using assoc_end_min_less_eq_max apply blast
using association_ends_unique by blast
end
(*** Simplification Rules ***************************************************)
section ‹ Simplification Rules›
lemma ex_alt_simps [simp]:
"∃ a. a"
"∃ a. ¬ a"
"(∃ a. (a ⟶ P) ∧ a) = P"
"(∃ a. ¬ a ∧ (¬ a ⟶ P)) = P"
by auto
declare numeral_eq_enat [simp]
lemmas basic_type_le_less [simp] = Orderings.order_class.le_less
for x y :: "'a basic_type"
declare element_type_alt_simps [simp]
declare update_element_type.simps [simp]
declare to_unique_collection.simps [simp]
declare to_nonunique_collection.simps [simp]
declare to_ordered_collection.simps [simp]
declare assoc_end_class_def [simp]
declare assoc_end_min_def [simp]
declare assoc_end_max_def [simp]
declare assoc_end_ordered_def [simp]
declare assoc_end_unique_def [simp]
declare oper_name_def [simp]
declare oper_context_def [simp]
declare oper_params_def [simp]
declare oper_result_def [simp]
declare oper_static_def [simp]
declare oper_body_def [simp]
declare oper_in_params_def [simp]
declare oper_out_params_def [simp]
declare assoc_end_type_def [simp]
declare oper_type_def [simp]
declare op_type_alt_simps [simp]
declare typing_alt_simps [simp]
declare normalize_alt_simps [simp]
declare nf_typing.simps [simp]
declare subclass1.intros [intro]
declare less_classes1_def [simp]
declare literals_classes1_def [simp]
lemma attribute_Employee_name [simp]:
"attribute Employee STR ''name'' D τ =
(D = Employee ∧ τ = String[1])"
proof -
have "attribute Employee STR ''name'' Employee String[1]"
by eval
thus ?thesis
using attribute_det by blast
qed
lemma association_end_Project_members [simp]:
"association_end Project None STR ''members'' D τ =
(D = Project ∧ τ = (Employee, 1, 20, True, True))"
proof -
have "association_end Project None STR ''members''
Project (Employee, 1, 20, True, True)"
by eval
thus ?thesis
using association_end_det by blast
qed
lemma association_end_Employee_projects_simp [simp]:
"association_end Employee None STR ''projects'' D τ =
(D = Employee ∧ τ = (Project, 0, ∞ , False, True))"
proof -
have "association_end Employee None STR ''projects''
Employee (Project, 0, ∞ , False, True)"
by eval
thus ?thesis
using association_end_det by blast
qed
lemma static_operation_Project_allProjects [simp]:
"static_operation ⟨ Project⟩ \ <T>[1] STR ''allProjects'' [] oper =
(oper = (STR ''allProjects'', ⟨ Project⟩ \ <T>[1], [], Set ⟨ Project⟩ \ <T>[1], True,
Some (MetaOperationCall ⟨ Project⟩ \ <T>[1] AllInstancesOp)))"
proof -
have "static_operation ⟨ Project⟩ \ <T>[1] STR ''allProjects'' []
(STR ''allProjects'', ⟨ Project⟩ \ <T>[1], [], Set ⟨ Project⟩ \ <T>[1], True,
Some (MetaOperationCall ⟨ Project⟩ \ <T>[1] AllInstancesOp))"
by eval
thus ?thesis
using static_operation_det by blast
qed
(*** Basic Types ************************************************************)
section ‹ Basic Types›
subsection ‹ Positive Cases›
lemma "UnlimitedNatural < (Real :: classes1 basic_type)" by simp
lemma "⟨ Employee⟩ \ <T> < ⟨ Person⟩ \ <T>" by auto
lemma "⟨ Person⟩ \ <T> ≤ OclAny" by simp
subsection ‹ Negative Cases›
lemma "¬ String ≤ (Boolean :: classes1 basic_type)" by simp
(*** Types ******************************************************************)
section ‹ Types›
subsection ‹ Positive Cases›
lemma "Integer[?] < (OclSuper :: classes1 type)" by simp
lemma "Collection Real[?] < (OclSuper :: classes1 type)" by simp
lemma "Set (Collection Boolean[1]) < (OclSuper :: classes1 type)" by simp
lemma "Set (Bag Boolean[1]) < Set (Collection Boolean[?] :: classes1 type)"
by simp
lemma "Tuple (fmap_of_list [(STR ''a'', Boolean[1]), (STR ''b'', Integer[1])]) <
Tuple (fmap_of_list [(STR ''a'', Boolean[?] :: classes1 type)])" by eval
lemma "Integer[1] ⊔ (Real[?] :: classes1 type) = Real[?]" by simp
lemma "Set Integer[1] ⊔ Set (Real[1] :: classes1 type) = Set Real[1]" by simp
lemma "Set Integer[1] ⊔ Bag (Boolean[?] :: classes1 type) = Collection OclAny[?]"
by simp
lemma "Set Integer[1] ⊔ (Real[1] :: classes1 type) = OclSuper" by simp
subsection ‹ Negative Cases›
lemma "¬ OrderedSet Boolean[1] < Set (Boolean[1] :: classes1 type)" by simp
(*** Typing *****************************************************************)
section ‹ Typing›
subsection ‹ Positive Cases›
text ‹
🍋 ‹ E1::A : E1[1]› ›
lemma
"Γ0 ⊨ EnumLiteral STR ''E1'' STR ''A'' : (Enum STR ''E1'')[1]"
by simp
text ‹
🍋 ‹ true or false : Boolean[1]› ›
lemma
"Γ0 ⊨ OperationCall (BooleanLiteral True) DotCall OrOp
[BooleanLiteral False] : Boolean[1]"
by simp
text ‹
🍋 ‹ null and true : Boolean[?]› ›
lemma
"Γ0 ⊨ OperationCall (NullLiteral) DotCall AndOp
[BooleanLiteral True] : Boolean[?]"
by simp
text ‹
🍋 ‹ let x : Real[1] = 5 in x + 7 : Real[1]› ›
lemma
"Γ0 ⊨ Let (STR ''x'') (Some Real[1]) (IntegerLiteral 5)
(OperationCall (Var STR ''x'') DotCall PlusOp [IntegerLiteral 7]) : Real[1]"
by simp
text ‹
🍋 ‹ null.oclIsUndefined() : Boolean[1]› ›
lemma
"Γ0 ⊨ OperationCall (NullLiteral) DotCall OclIsUndefinedOp [] : Boolean[1]"
by simp
text ‹
🍋 ‹ Sequence{1..5, null}.oclIsUndefined() : Sequence(Boolean[1])› ›
lemma
"Γ0 ⊨ OperationCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5),
CollectionItem NullLiteral])
DotCall OclIsUndefinedOp [] : Sequence Boolean[1]"
by simp
text ‹
🍋 ‹ Sequence{1..5}->product(Set{'a', 'b'})
: Set(Tuple(first: Integer[1], second: String[1]))› ›
lemma
"Γ0 ⊨ OperationCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5)])
ArrowCall ProductOp
[CollectionLiteral SetKind
[CollectionItem (StringLiteral ''a''),
CollectionItem (StringLiteral ''b'')]] :
Set (Tuple (fmap_of_list [
(STR ''first'', Integer[1]), (STR ''second'', String[1])]))"
by simp
text ‹
🍋 ‹ Sequence{1..5, null}?->iterate(x, acc : Real[1] = 0 | acc + x)
: Real[1]› ›
lemma
"Γ0 ⊨ IterateCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5),
CollectionItem NullLiteral]) SafeArrowCall
[STR ''x''] None
(STR ''acc'') (Some Real[1]) (IntegerLiteral 0)
(OperationCall (Var STR ''acc'') DotCall PlusOp [Var STR ''x'']) : Real[1]"
by simp
text ‹
🍋 ‹ Sequence{1..5, null}?->max() : Integer[1]› ›
lemma
"Γ0 ⊨ OperationCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5),
CollectionItem NullLiteral])
SafeArrowCall CollectionMaxOp [] : Integer[1]"
by simp
text ‹
🍋 ‹ let x : Sequence(String[?]) = Sequence{'abc', 'zxc'} in
->any(it | it = 'test') : String[?]› ›
lemma
"Γ0 ⊨ Let (STR ''x'') (Some (Sequence String[?]))
(CollectionLiteral SequenceKind
[CollectionItem (StringLiteral ''abc''),
CollectionItem (StringLiteral ''zxc'')])
(AnyIteratorCall (Var STR ''x'') ArrowCall
[STR ''it''] None
(OperationCall (Var STR ''it'') DotCall EqualOp
[StringLiteral ''test''])) : String[?]"
by simp
text ‹
🍋 ‹ let x : Sequence(String[?]) = Sequence{'abc', 'zxc'} in
?->closure(it | it) : OrderedSet(String[1])› ›
lemma
"Γ0 ⊨ Let STR ''x'' (Some (Sequence String[?]))
(CollectionLiteral SequenceKind
[CollectionItem (StringLiteral ''abc''),
CollectionItem (StringLiteral ''zxc'')])
(ClosureIteratorCall (Var STR ''x'') SafeArrowCall
[STR ''it''] None
(Var STR ''it'')) : OrderedSet String[1]"
by simp
text ‹
🍋 ‹ context Employee:
: String[1]› ›
lemma
"Γ0 (STR ''self'' ↦ f Employee[1]) ⊨
AttributeCall (Var STR ''self'') DotCall STR ''name'' : String[1]"
by simp
text ‹
🍋 ‹ context Employee:
: Set(Project[1])› ›
lemma
"Γ0 (STR ''self'' ↦ f Employee[1]) ⊨
AssociationEndCall (Var STR ''self'') DotCall None
STR ''projects'' : Set Project[1]"
by simp
text ‹
🍋 ‹ context Employee:
.members : Bag(Employee[1])› ›
lemma
"Γ0 (STR ''self'' ↦ f Employee[1]) ⊨
AssociationEndCall (AssociationEndCall (Var STR ''self'')
DotCall None STR ''projects'')
DotCall None STR ''members'' : Bag Employee[1]"
by simp
text ‹
🍋 ‹ Project[?].allInstances() : Set(Project[?])› ›
lemma
"Γ0 ⊨ MetaOperationCall Project[?] AllInstancesOp : Set Project[?]"
by simp
text ‹
🍋 ‹ Project[1]::allProjects() : Set(Project[1])› ›
lemma
"Γ0 ⊨ StaticOperationCall Project[1] STR ''allProjects'' [] : Set Project[1]"
by simp
subsection ‹ Negative Cases›
text ‹
🍋 ‹ true = null› ›
lemma
"∄ τ. Γ0 ⊨ OperationCall (BooleanLiteral True) DotCall EqualOp
[NullLiteral] : τ"
by simp
text ‹
🍋 ‹ let x : Boolean[1] = 5 in x and true› ›
lemma
"∄ τ. Γ0 ⊨ Let STR ''x'' (Some Boolean[1]) (IntegerLiteral 5)
(OperationCall (Var STR ''x'') DotCall AndOp [BooleanLiteral True]) : τ"
by simp
text ‹
🍋 ‹ let x : Sequence(String[?]) = Sequence{'abc', 'zxc'} in
->closure(it | 1)› ›
lemma
"∄ τ. Γ0 ⊨ Let STR ''x'' (Some (Sequence String[?]))
(CollectionLiteral SequenceKind
[CollectionItem (StringLiteral ''abc''),
CollectionItem (StringLiteral ''zxc'')])
(ClosureIteratorCall (Var STR ''x'') ArrowCall [STR ''it''] None
(IntegerLiteral 1)) : τ"
by simp
text ‹
🍋 ‹ Sequence{1..5, null}->max()› ›
lemma
"∄ τ. Γ0 ⊨ OperationCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5),
CollectionItem NullLiteral])
ArrowCall CollectionMaxOp [] : τ"
proof -
have "¬ operation_defined (Integer[?] :: classes1 type) STR ''max'' [Integer[?]]"
by eval
thus ?thesis by simp
qed
(*** Code *******************************************************************)
section ‹ Code›
subsection ‹ Positive Cases›
values "{(D , τ). attribute Employee STR ''name'' D τ}"
values "{(D , end). association_end Employee None STR ''employees'' D end}"
values "{(D , end). association_end Employee (Some STR ''project_manager'') STR ''employees'' D end}"
values "{op. operation Project[1] STR ''membersCount'' [] op}"
values "{op. operation Project[1] STR ''membersByName'' [String[1]] op}"
value "has_literal STR ''E1'' STR ''A''"
text ‹
🍋 ‹ context Employee:
.members : Bag(Employee[1])› ›
values
"{τ. Γ0 (STR ''self'' ↦ f Employee[1]) ⊨
AssociationEndCall (AssociationEndCall (Var STR ''self'')
DotCall None STR ''projects'')
DotCall None STR ''members'' : τ}"
subsection ‹ Negative Cases›
values "{(D , τ). attribute Employee STR ''name2'' D τ}"
value "has_literal STR ''E1'' STR ''C''"
text ‹
🍋 ‹ Sequence{1..5, null}->max()› ›
values
"{τ. Γ0 ⊨ OperationCall (CollectionLiteral SequenceKind
[CollectionRange (IntegerLiteral 1) (IntegerLiteral 5),
CollectionItem NullLiteral])
ArrowCall CollectionMaxOp [] : τ}"
end
Messung V0.5 in Prozent C=84 H=96 G=90
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland