Multilingual Syntax Editing for Software Specifications

Transcription

Multilingual Syntax Editing for Software Specifications
Multilingual Syntax Editing for Software
Specifications
Hans-Joachim Daniels
30. August 2005
Diplomarbeit
Universität Karlsruhe (TH)
Fakultät für Informatik
Institut für Theoretische Informatik
Verantwortlicher Betreuer: Prof. Dr. Peter H. Schmitt
Betreuer: Kristofer Johannisson, Aarne Ranta
Danksagungen
Mein erster Dank gebührt Aarne Ranta, der den Anstoß zu dieser Arbeit im Ausland
gegeben hat und mir dort hilfreich zur Seite stand. Besonderen Dank auch an Kristofer
Johanisson, der mich auch nach Ablauf seiner Anstellung bei Chalmers weiter sehr gut
betreut hat. Vielen Dank auch an Peter Schmitt, der es mir ermöglicht hat, diese Diplomarbeit in Göteborg zu schreiben.
Janna Khegais Antworten haben mir am Anfang beim Verständnis des Editor-Quelltextes
viel Zeit gespart. Vielen Dank.
Meiner Mutter danke ich dafür, dass sie viele sprachliche Fehler in dieser Ausarbeitung aufgedeckt hat. Und überhaupt, ohne die Unterstützung meiner Eltern hätte ich
mein Studium und meine Auslandsaufenthalte nicht machen können. Vielen, vielen Dank
dafür.
ii
Erklärung
Hiermit versichere ich, die vorliegende Arbeit selbständig verfasst und keine anderen als
die angegebenen Quellen und Hilfsmittel benutzt zu haben.
Göteborg, den 30. August 2005
Hans-Joachim Daniels
iii
iv
Inhaltsverzeichnis
I. Deutsche Zusammenfassung
1
1. Einführung
1.1. Das Grammatical Framework . . . . . . . . .
1.2. KeY . . . . . . . . . . . . . . . . . . . . . . .
1.3. Formal and Informal Software Specification“
”
1.4. Ziel . . . . . . . . . . . . . . . . . . . . . . . .
1.5. Zielgruppe und mögliche Anwendungsgebiete .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
4
4
5
5
2. Gebrauchstauglichkeitsprobleme in der alten Version des Editors
2.1. Welche Funktion macht was? . . . . . . . . . . . . . . . . . . . . .
2.2. Den Wald vor lauter Bäumen nicht sehen . . . . . . . . . . . . . .
2.3. Was soll ich jetzt machen? . . . . . . . . . . . . . . . . . . . . . .
2.4. Alles ist rot, was habe ich falsch gemacht? . . . . . . . . . . . . .
2.5. So viele Vergleichsoperatoren? . . . . . . . . . . . . . . . . . . . .
2.6. Wo fängt was an? . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.7. Aber ich wollte doch das nächste Fragezeichen ausfüllen . . . . . .
2.8. Ich will 42 eingeben. . . . . . . . . . . . . . . . . . . . . . . . . .
2.9. Aber meine Ganze Zahl ist doch eine Reele Zahl, oder etwa nicht?
2.10. Wo sind meine booleschen Eigenschaften hin? . . . . . . . . . . .
2.11. Und warum klappt der Baum immer zusammen? . . . . . . . . .
2.12. Was machten self und result da? . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
7
7
8
8
8
8
9
9
9
10
10
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3. Systemarchitektur
11
3.1. Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2. Integrationsprobleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4. Gebrauchstauglichkeitsverbesserungen
4.1. Die nächste Verfeinerung . . . . . .
4.2. Unterstützung für HTML . . . . .
4.3. Typumwandlungen . . . . . . . . .
4.4. Kleinere Verbesserungen . . . . . .
4.4.1. self und result . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
14
14
15
15
v
Inhaltsverzeichnis
4.4.2.
4.4.3.
4.4.4.
4.4.5.
4.4.6.
Einfachere Erreichbarkeit von Eigenschaften von self . .
Wegkommen von verstecken Parametern . . . . . . . . .
Geänderte Auswahlfarbe . . . . . . . . . . . . . . . . . .
Zerteilfenster per Mittelklick . . . . . . . . . . . . . . . .
Sicherheitsabfrage beim Beenden, ob man speichern will
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15
15
16
16
17
5. Fazit
19
5.1. Nur ein Editor speziell für OCL? . . . . . . . . . . . . . . . . . . . . . . 19
5.2. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
II. Englische Ausarbeitung
23
6. Introduction
6.1. The Grammatical Framework . . . . . . . .
6.1.1. Overview . . . . . . . . . . . . . . .
6.1.2. Example . . . . . . . . . . . . . . . .
6.1.3. The Java Editor for GF . . . . . . .
6.2. KeY . . . . . . . . . . . . . . . . . . . . . .
6.3. ‘Formal and Informal Software Specification’
6.4. Goals . . . . . . . . . . . . . . . . . . . . . .
6.5. Potential use and users . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
25
25
25
26
29
30
33
35
35
7. Usability Problems of the Previous Editor
7.1. Which function does what? . . . . . . . . . .
7.2. Not seeing the forest because of so many trees
7.3. What am I to do? . . . . . . . . . . . . . . . .
7.4. Where does what begin? . . . . . . . . . . . .
7.5. But my Integer is a Real, isn’t it? . . . . . . .
7.6. I want to enter 42. . . . . . . . . . . . . . . .
7.7. Where is my boolean property? . . . . . . . .
7.8. Everything is red, what did I do wrong? . . .
7.9. So many comparison operators? . . . . . . . .
7.10. I wanted to fill in the next question mark . . . .
7.11. I could choose self, but now nothing is offered.
7.12. Why does the tree always collapse my nodes?
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
37
37
38
39
40
42
43
44
45
46
46
47
.
.
.
.
49
49
50
51
53
8. System Architecture
8.1. Overview . . . . . . . . . . . . . . .
8.2. State of the affairs at the beginning
8.3. Support for the new grammars . . .
8.4. Basic work-flow . . . . . . . . . . .
vi
. . . . . . .
of this work
. . . . . . .
. . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Inhaltsverzeichnis
9. Usability Improvements
9.1. The next refinement ... . . . . . . . . . . . .
9.1.1. Refinement descriptions . . . . . . .
9.1.2. Grouping of refinement menu entries
9.1.3. Parameter descriptions . . . . . . . .
9.1.4. Printnames as the solution . . . . . .
9.2. HTML support . . . . . . . . . . . . . . . .
9.3. Coercions . . . . . . . . . . . . . . . . . . .
9.3.1. Where to insert? . . . . . . . . . . .
9.3.2. How to tag? . . . . . . . . . . . . . .
9.3.3. Refining . . . . . . . . . . . . . . . .
9.3.4. Deleting . . . . . . . . . . . . . . . .
9.3.5. Wrapping . . . . . . . . . . . . . . .
9.3.6. Changes afterwards . . . . . . . . . .
9.3.7. Collection subtyping . . . . . . . . .
9.3.8. Summing up coercions . . . . . . . .
9.4. Minor Improvements . . . . . . . . . . . . .
9.4.1. Suppression of self and result . . . . .
9.4.2. Easier access to properties of self . .
9.4.3. Comparison operators . . . . . . . .
9.4.4. Escaping hidden arguments . . . . .
9.4.5. Changed selection colour . . . . . . .
9.5. Generic Improvements . . . . . . . . . . . .
9.5.1. Middle-click parsing . . . . . . . . .
9.5.2. Ask to save before quitting . . . . . .
9.6. Unimplemented ideas . . . . . . . . . . . . .
9.6.1. On AtomSent . . . . . . . . . . . . .
9.6.2. The collapsing tree . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
55
55
55
57
58
63
65
66
67
69
69
71
72
76
77
79
80
80
80
82
83
84
84
84
86
86
86
87
10.Implementation
10.1. Refactoring the old version of the editor . . . . . .
10.1.1. Documentation . . . . . . . . . . . . . . . .
10.1.2. Names . . . . . . . . . . . . . . . . . . . . .
10.1.3. Data flow . . . . . . . . . . . . . . . . . . .
10.1.4. Static attributes . . . . . . . . . . . . . . . .
10.1.5. Division of labour . . . . . . . . . . . . . . .
10.1.6. Character counting for click-in functionality
10.2. GF’s XML . . . . . . . . . . . . . . . . . . . . . . .
10.2.1. <hmsg> . . . . . . . . . . . . . . . . . . . .
10.2.2. <linearizations> . . . . . . . . . . . . . . .
10.2.3. <tree> . . . . . . . . . . . . . . . . . . . . .
10.2.4. <message> . . . . . . . . . . . . . . . . . .
10.2.5. <menu> . . . . . . . . . . . . . . . . . . . .
10.3. Overview over the classes . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
89
89
89
90
90
91
91
92
93
93
93
95
96
96
96
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
vii
Inhaltsverzeichnis
10.4. Receiving the state . . . . .
10.4.1. XML processing . . .
10.4.2. Probing . . . . . . .
10.4.3. Undo handling . . .
10.5. Sending commands . . . . .
10.6. Integration into TogetherCC
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
96
96
101
101
102
102
11.Conclusions
11.1. User perspective . . . . . . . . .
11.2. Development . . . . . . . . . .
11.3. Contributions . . . . . . . . . .
11.4. Only a specialised OCL editor?
11.5. In the view of EN ISO 9241 - 10
11.6. Future work . . . . . . . . . . .
11.7. Related work . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
105
105
105
106
106
107
109
114
.
.
.
.
.
115
115
116
118
118
119
A. Format of the Printnames
A.1. Introduction . . . . . . . . . . . .
A.2. How to use the enhanced tooltips
A.2.1. Tooltips . . . . . . . . . .
A.2.2. Grouping . . . . . . . . .
A.2.3. Parameter descriptions . .
viii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Abbildungsverzeichnis
6.1. The AST and linearisation of orS in the graphical editor for GF . .
6.2. The parametrised equality operator eq . . . . . . . . . . . . . . . .
6.3. The main window of the Java GUI for GF . . . . . . . . . . . . . .
6.4. State after a refinement . . . . . . . . . . . . . . . . . . . . . . . . .
6.5. Selection of an already refined node . . . . . . . . . . . . . . . . . .
6.6. Tree and linearisation after sending the delete command d . . . . .
6.7. Tree and linearisation after sending the change command ch Thick
6.8. Tree and linearisation after sending the wrap command w Dirty . .
6.9. Tree and linearisation after sending the peel head command ph . . .
6.10. Context menu integration into TogetherCC . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
27
28
30
31
32
32
32
32
33
34
7.1.
7.2.
7.3.
7.4.
7.5.
7.6.
Screen of the editor directly after starting . . . . . . . . . .
The hidden argument of eq is selected. But what does it do?
A non-trivial OCL constraint of the PayCard example . . . .
A transitive subtype witness . . . . . . . . . . . . . . . . . .
An AST, where a boolean model element is accessed. . . . .
The user refined with self for a wrong type and is now stuck.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
38
39
40
42
44
47
8.1.
8.2.
8.3.
8.4.
System architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
An example OCL file as expected by the OCL parser . . . . . . . . . . .
An example OCL skeleton for the method charge as a GF AST . . . . .
The editor after choosing Edit Pre/Postcondition [GF] in the context
menu of TogetherCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
52
52
The situation from 7.1, but this time with English as the menu language
The OCL collection operation forAll, explained with a tooltip . . . . . . .
First argument of implies selected . . . . . . . . . . . . . . . . . . . . . .
First argument of propCall selected . . . . . . . . . . . . . . . . . . . . .
First argument of propCall unrefined, all properties of all classes listed . .
First argument of propCall refined, only the properties of that class are
listed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.7. GF refines some type arguments automatically . . . . . . . . . . . . . . .
9.8. Figure 9.4 revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.9. A picture in the HTML display . . . . . . . . . . . . . . . . . . . . . . .
9.10. Side by side comparison of no formatting and formatting . . . . . . . . .
56
57
59
59
60
9.1.
9.2.
9.3.
9.4.
9.5.
9.6.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
54
61
61
62
66
67
ix
Abbildungsverzeichnis
9.11. Different expectations for coerce . . . . . . . . . . . . . . . .
9.12. Before and after an automatic coerce application . . . . . . .
9.13. Before and after a delete of a coerced Instance . . . . . . . .
9.14. Deleting of coerce above an unrefined node . . . . . . . . . .
9.15. Before and after wrapping with w wrapper 2 . . . . . . . .
9.16. Wrapping without CoercedTo . . . . . . . . . . . . . . . . .
9.17. Wrapping with needed coerce, but coerce hidden . . . . . . .
9.18. The situation from figure 9.17, but with coerce shown . . . .
9.19. GF constraints make the coerce visible . . . . . . . . . . . .
9.20. The editor noticed, that a subtyping witness is missing . . .
9.21. Direct access to the suiting properties of self, here for Integer
9.22. Middle-click parsing with the last linearization language . .
9.23. Middle-click parsing with the clicked linearization language .
9.24. Safety question for saving when exiting . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10.1. Sample of the XML for a GF state . . . . . . . . . . .
10.2. The editor after receiving the XML from figure 10.1. .
10.3. The main Java classes of the editor . . . . . . . . . . .
10.4. The classes for the refinement menu entries . . . . . . .
10.5. The behind-the-scenes probing classes . . . . . . . . . .
10.6. The classes, that interface the editor with TogetherCC
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 94
. 95
. 97
. 98
. 99
. 103
The refinement menu of the OCL grammars . . . . . . . . .
Grouping, tooltips and parameter descriptions . . . . . . . .
Parameter description as tooltip in the abstract syntax tree .
Better text for submenus of the refinement menu . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
A.1.
A.2.
A.3.
A.4.
x
.
.
.
.
.
.
.
.
.
.
.
.
68
70
71
72
72
74
75
75
77
78
81
85
86
87
116
117
117
119
Teil I.
Mehrsprachiges syntaxgeleitetes
Verfassen von Software-Spezifikationen
— Deutsche Zusammenfassung —
1
1. Einführung
Diese Arbeit ist im Bereich der Softwarespezifikation angesiedelt. Zu sagen, dass ein
Programm das tut, was es soll, geht nur, wenn klar ist, was es tun soll. Und gerade im
Bereich formaler Methoden reicht es nicht, wenn nur einem Menschen klar ist, welche
Anforderungen an ein Programm gestellt werden sollen. Hier müssen diese Anforderungen formalisiert sein. Dafür werden besondere Spezifikationssprachen verwendet. Eine
davon ist die Object Constraint Language“ OCL.
”
Diejenige Sprache jedoch, die in der Praxis am meisten für die Spezifikation von Software verwendet wird, ist die Sprache, die die Entwickler oder die Anforderungsschreiber
normal auch sprechen. Formale Sprachen sind dagegen nicht so weit verbreitet. Die Absicht der dieser Ausarbeitung zugrundeliegende Arbeit ist es, die Kluft zwischen gewollt
(formal und exakt spezifiziert) und gegeben (in natürlicher Sprache, meist mehrdeutig,
beschrieben) zu überbrücken.
In diesem Abschnitt werden erst die in dieser Arbeit verwendeten Werkzeuge beschrieben. Später wird darauf eingegangen, woher diese Arbeit ihre Berechtigung hat. Auch
das Thema wird erläutert.
1.1. Das Grammatical Framework
GF ist ein Rahmenwerk, um Grammatiken zu schreiben und zu benutzen. Aber es ist
nicht nur einfach ein Grammatikformulismus, wie z.B. die Backus-Naur-Form einer ist,
sondern es ist auch noch die praktische Umsetzung dazu. Diese umfasst u.a. das Generieren und Zerteilen von Texten und die Typprüfung von abstrakten Syntaxbäumen.
GF ist für sowohl für formale Sprachen, wie auch für (genau beschriebene Teile von)
natürlichen Sprachen geeignet. Seine Fähigkeiten gehen über die reine Syntaxbehandlung hinaus; auch auf die Semantik kann, besonders mit Hilfe der abhängenden Typen
aus Martin Löfs Typtheorie, eingegangen werden.
Das Hauptmerkmal von GF ist die Trennung von abstraktem Syntaxbaum und seiner
Darstellung in konkreten Sprachen. Dadurch, dass der Aufbau einer Sprache getrennt
von ihrer Darstellung beschrieben wird, kann ihre Darstellung in verschiedenen Sprachen auch verschieden beschrieben werden. Zudem können sich so im laufenden Betrieb
mehrere konkrete Sprachen den gleichen Syntaxbaum teilen. Das ermöglicht, dass ein
Text, dessen abstrakte Syntax von GF verwaltet wird, in mehreren Sprachen ausgegeben werden kann, wie es z.B. in Abbildung 6.5 ersichtlich ist. Wie die abstrakte Syntax
und ihre Darstellungsregeln aussehen können, ist im Beispiel in Abschnitt 6.1.2 gezeigt.
3
1. Einführung
Dort wird auch näher auf den Begriff der abhängenden Typen eingegangen. Die in dieser
Arbeit verwendeten konkreten Sprachen sind OCL, Englisch und Deutsch.
Zu GF gehört auch eine in Java geschriebene graphische Benutzeroberfläche. Alle Befehle, die man dem Editor gibt, werden auf dem abstrakten Syntaxbaum ausgeführt.
Und dieser wird wiederum in allen geladenen konkreten Sprachen (die zur abstrakten
Grammatik passen müssen) dargestellt. Dadurch kann man Texte in Sprachen erstellen,
die man selbst nicht kennt. Aber da alle Sprachen sich einen Syntaxbaum teilen, hat der
Text in ihnen den gleichen Inhalt. Es reicht also, eine der beteiligten konkreten Sprachen
zu kennen (sofern man natürlich den Schreibern der konkreten Grammatiken zutraut,
gute Arbeit geleistet zu haben).
Die einzelnen Befehle, die der Editor beherrscht, sind in Abschnitt 6.1.3 aufgezählt und
erläutert.
1.2. KeY
Die Umsetzung dieser Ausarbeitung ist Teil des KeY-Systems. Dieses hat zum Ziel, formale Softwarespezifizierung und -verifikation in den industriellen Softwareentwicklungsprozess zu integrieren. Der Gedanke dahinter ist, dass Verfikationswerkzeuge außerhalb
einer normalen Entwicklungsumgebung in der Praxis keine weite Verbreitung finden werden. Dazu gehört auch, dass keine esoterische Programmiersprache unterstützt wird, sondern eine Sprache, die Entwickler sowieso schon nutzen. Daher wurde JavaCard gewählt.
Zudem integriert sich KeY in das kommerziele CASE-Werkzeug Together Control Center
von Borland.
KeY verwendet als Spezifikationssprache, aus der später die Beweisverpflichtungen generiert werden, OCL, welche Teil von UML ist. Dies ist nötig, da UML alleine noch
relativ unpräzise ist. In OCL können Klasseninvarianten und Vor- und Nachbedingungen für Methoden definiert werden. Ein großes Problem ist jedoch, dass, obwohl UML
selbst sehr weit verbreitet ist, OCL kaum bekannt ist. Daher unterstützt KeY den Benutzer bei der Erstellung von Einschränkungen1 in OCL. Ein verwendeter Ansatz dazu
ist in [Bub02] beschrieben. Der dagegen dieser Arbeit zugrundeliegende Ansatz wird im
folgenden Absatz beschrieben.
1.3.
Formal and Informal Software Specification“
”
Die Doktorarbeit Formal and Informal Software Specification“ ([Johar]) von Kristofer
”
Johanisson stellt den direkten Rahmen dieser Diplomarbeit dar. Im folgenden wird ein
kurzer Überblick über sie gegeben.
1
4
In dieser deutschen Zusammenfassung der englischen Ausarbeitungen werden die UML-Fachbegriffe
durch die auf http://www.oose.de/uml_auf_deutsch.htm angegebenen wiedergegeben.
1.4. Ziel
In [HJR02] (ein Teil von [Joh05]) wird ein System beschrieben, das eine Brücke zwischen der formalen Sprache OCL und der natürlichen Sprache Englisch schlägt. Als
Grammatikformalismus wird dabei GF verwendet. Kristofer Johanisson schrieb damit
eine gemeinsame abstrakte Grammatik für OCL und Englisch, sowie die dazugehörigen
konkreten Grammatiken. Damit war es mit dem gewöhnlichen graphischen Editor für GF
möglich, Einschränkungen in OCL zu verfassen, und das, ohne OCL können zu müssen.
In [Dan03] wurde dann mit Hilfe der Grammatikresourcen ( resource grammars“, siehe
”
6.1.1 für mehr Details) eine deutsche Grammatik für OCL geschrieben. Der Autor hat
diese später ins Englische übertragen. David Burke hat sie dann in seiner Master-Arbeit
verbessert und die Unterstützung von Formatierungen eingeführt ([BJ05] (ebenfalls Teil
von [Joh05])). Diese hat der Autor dann wieder ins Deutsche übertragen.
In einem weiteren Teil seiner Doktorarbeit, [Joh04], wird ein Zerteiler für OCL beschrieben. Erst dadurch wurde es möglich, OCL einzulesen und ins Englische und Deutsche
zu übersetzen. Vorher war nur das mehrsprachige Verfassen möglich, da der allgemeine
Zerteiler, den GF bereitstellt, eine Reihe von Einschränkungen besitzt.
Das Hauptproblem des Syntaxeditor war, neben Integrationsproblemen, dass er sehr
umständlich zu benutzen war.
1.4. Ziel
Und diesem Mangel an Gebrauchstauglichkeit abzuhelfen, ist das Thema dieser Diplomarbeit. Die Grundidee ist es, einen Editor zu schaffen, mit dem ein Neuling, der von
OCL keine Ahnung haben zu braucht, Einschränkung in eben dieser formalen Sprache
erschaffen kann.
Dazu gilt es, zuerst die Probleme, die ein Anwender haben kann, zu identifizieren. Dann
sollten Lösungen dafür ersonnen und schlussendlich auch umgesetzt werden. Kurz, der
Editor soll gebrauchstauglich werden.
1.5. Zielgruppe und mögliche Anwendungsgebiete
Der Editor ist nicht für OCL-Kenner gedacht. Der grammatikbasierte Ansatz, in dem
von oben nach unten in einem Syntaxbaum verfeinert wird, ist gänzlich anders, als
OCL-Einschränkungen in einem textbasierten Editor zu schreiben. Nur aus typkorrekten
Verfeinerungen auswählen zu können, ist eine Einschränkung, die nicht unbedingt durch
die Hilfestellung, die eben diese Einschränkung auch sein kann, ausgeglichen werden
kann.
Hauptsächlich profitieren werden davon wohl OCL-Unerfahrene, die sich jedoch im Problembereich gut auskennen und deshalb OCL-Einschränkungen verfassen sollen. Da
OCL zusätzlich angezeigt wird, kann der Editor auch als Lernhilfe verstanden werden.
Wie verschiedene angebotene OCL-Funktionen in OCL aussehen, kann gelernt werden,
5
1. Einführung
und später unabhängig vom Editor angewendet werden. Daher ist auch die Lehre ein
mögliches Anwendungsgebiet.
6
2. Gebrauchstauglichkeitsprobleme in
der alten Version des Editors
Anzumerken ist, dass dieser Arbeit keine echte Gebrauchstauglichkeitsstudie oder Experimente mit möglichen Benutzern zu Grunde liegen. Der Grund dafür liegt darin, dass
der Autor zu viele Hemmschuhe ausgemacht hat, die die Benutzer vor zu große Probleme
stellen würden. Seiner Überzeugung nach sollten erst diese aus dem Weg geräumt werden, bevor in Experimenten tiefer liegende Probleme zu Tage gebracht werden können.
2.1. Welche Funktion macht was?
Der alte Editor hat einen mit einer Riesenliste von zum Teil unverständlichen Funktionsnamen wie in Abbildung 7.1 begrüßt. Manche dieser Namen lassen sich zwar verstehen,
aber ein Name erklärt trotzdem noch nicht, was eine Funktion macht, wenn man über
einfache Vergleiche hinausgeht. any ist so ein Beispiel. Da es die Aufgabe des Editors ist,
OCL-unerfahrenen Benutzern OCL zu ermöglichen, kann nicht davon ausgegangen werden, dass Funktionen anhand ihres Namens wiedererkannt werden. Funktionen müssen
daher beschrieben sein.
2.2. Den Wald vor lauter Bäumen nicht sehen
Die Liste der möglichen Verfeinerungen kann über 50 Einträge lang werden. Was schon
ziemlich unübersichtlich ist, da dann eine gewünschte Funktion, deren Namen man nicht
kennt, sondern höchstens erkennen kann, herauszusuchen. Besonders, wenn viele Funktionen gar nichts mit der gesuchten zu tun haben, wie z.B. Vergleichsfunktionen gegenüber Funktionen, die etwas über die Elemente von OCL-Sammlungen aussagen. Diese
Liste sollte verkleinert und dem Benutzer bei der Auswahl geholfen werden.
2.3. Was soll ich jetzt machen?
Nicht immer sorgt die Baumstruktur oder die Darstellung des Baumes im Englischen
oder Deutschen für Klarheit, wie es z.B. für die und-Funktion andS der Fall ist, deren
zwei Satz-Argumente wohl die beiden mit und“ verknüpften Sätze darstellen werden.
”
7
2. Gebrauchstauglichkeitsprobleme in der alten Version des Editors
Besonders versteckte Typ-Parameter, wie sie im Beispiel zu GF in Abschnitt 6.1.2 beschrieben werden, stellen hier ein Problem dar. Was der Benutzer eigentlich auswählen
soll, wenn er (oder GF für ihn) so einen Knoten ausgewählt hat, wird ihm nicht mitgeteilt.
2.4. Alles ist rot, was habe ich falsch gemacht?
Der Benutzer wurde als erstes mit einem in der Signalfarbe Rot unterlegten Text wie
in Abbildung 7.1 begrüßt. Ihm gleich zu Beginn einen Fehler zu unterstellen, besonders,
wenn er unnötig ist, wie in Abschnitt 7.8 beschrieben, ist nicht besonders hilfreich.
2.5. So viele Vergleichsoperatoren?
In OCL sind die Vergleichsoperatoren für größer und kleiner nur für Reelle Zahlen definiert, in der Grammatik sind sie jedoch auch für Ganze Zahlen verfügbar. Sie kommen
also doppelt vor, auch wenn Integer ein Untertyp von Real in OCL ist, und daher die
Vergleichsoperatoren für Ganze Zahlen eigentlich unnötig sind.
Für die Gleichheitsoperatoren gibt es ganze 10, für alle vordefinierten Nicht-Sammlungstypen
von OCL je einen, dazu noch zwei verschiedene, die mittels eines Parameters für alle
Typen verfügbar sind. Warum so viele, wenn es doch auch weniger oder gar nur einer
tun?
2.6. Wo fängt was an?
Lange OCL-Einschränkungen ohne Formatierung oder Einrückung sind schwer lesbar,
wie Abbildung 7.3 zeigt. Und dieses Problem wurde bereits in [BJ05] für die verwendeten OCL-Grammatiken gelöst. Nur war diese Lösung nicht im Editor verwendbar, wie
Abschnitt 10.1.6 erklärt.
2.7. Aber ich wollte doch das nächste Fragezeichen
ausfüllen . . .
Wenn ein Fragezeichen im Editor ausgefüllt worden ist, wählt GF das nächste aus.
Allerdings ist die Definition von nächstes“ etwas unerwartet für den Benutzer, da GF
”
recht gerne den aktuellen Ast verlässt und damit den Teil, auf den der Benutzer sich
gerade konzentriert hat. Was nicht den Erwartungen des Benutzers enspricht.
8
2.8. Ich will 42 eingeben.
2.8. Ich will 42 eingeben.
Das Eingeben von eigenen Zahlen, wenn eine Instanz einer Ganzen Zahl erwartet wird,
ist nicht wirklich selbstbeschreibend. Funktionen für 0, 1 und 2 existieren zwar, aber
mehr nicht. Wenn der Benutzer doch über die Funktion int stolpert und sie auswählt,
bekommt er eine längere Liste. Aber immer noch keinen Hinweis, wie er eigene Zahlen
eingeben kann. Dass dazu nach der Auswahl von int der Read-Knopf betätigt werden
muss, gefolgt von der Auswahl von Term ist nicht ersichtlich.
Für Zeichenketten ist die Situation ähnlich.
2.9. Aber meine Ganze Zahl ist doch eine Reele Zahl,
oder etwa nicht?
Das Typsystem von OCL, das das objektorientierte von UML ist, kennt Vererbung. Eine
Instanz eines Untertyps ist immer anwendbar, wenn eine Instanz des Obertyps erwartet
wird. Das Typsystem von GF dagegen baut nicht auf Vererbung auf. Folglich muss dieses
Prinzip irgendwie nachgebildet werden. Dies geschieht mit Hilfe abhängender Typen, hat
aber das Problem, dass es explizit ist. Das heißt, der Benutzer muss selbst sagen, dass
jetzt eine Typumwandlung zum Obertyp stattfinden soll, während dies in UML/OCL
implizit passiert. Was sowohl umständlich und fehleranfällig ist und zudem auch mit der
Erwartung des Benutzers kollidiert.
Mehr zu dieser Thematik in Abschnitt 7.5.
2.10. Wo sind meine booleschen Eigenschaften hin?
Aussagen werden in den Grammatiken für OCL normalerweise in der GF-Kategorie Sent
wiedergegeben. Für boolsche Variablen existiert jedoch noch Instance BooleanC, das z.B.
als Parameter für Operationen aus dem UML-Modell verwendet werden muss. Abschnitt
7.7 geht noch näher auf diese Unterscheidung ein.
Das Problem mit Abfragen und Attributen vom Typ Boolean aus dem UML-Modell
ist, dass diese weder als Eigenschaft, noch als Sent oder Instance BooleanC auftauchen.
Sie werden getrennt davon behandelt und haben den Typ AtomSent, der nur über zwei
spezielle Funktionen erreichbar ist. Der Grund dafür ist eine schönere negierte Form,
die in der Übersetzung von OCL in natürliche Sprache ihre Stärke ausspielen kann. Im
Editor ist dies allerdings ein Hemmnis für den Benutzer, da dem Benutzer in keinster
Weise gezeigt wird, wie er boolsche Elemente aus dem Modell verwenden kann.
9
2. Gebrauchstauglichkeitsprobleme in der alten Version des Editors
2.11. Und warum klappt der Baum immer zusammen?
Wann immer der Benutzer einen anderen Knoten im abstrakten Syntaxbaum auswählt
(egal ob über einen Klick auf den Knoten im graphischen Baum oder durch die Auswahl
eines anderen Wortes in der Sprachdarstellung), wird der Baum komplett neu aufgebaut.
Dadurch geht unter anderem auch verloren, ob ein Knoten vorher aufgeklappt war, oder
nicht. Und das sorgt dafür, dass alle Knoten, außer dem aktuell ausgewählten, nach
jedem Knotenwechsel wieder zugeklappt sind. Was es dem Benutzer schwer macht, sich
im Baum zu orientieren, da dessen Aussehen nicht mehr dasselbe ist.
2.12. Was machten self und result da?
Die GF-Funktionen für self und result sind allgemein gehalten und bekommen ihren Typ
erst durch einen Klassenparameter. Damit sie nicht fälschlicherweise für unpassende
Klassen eingesetzt werden, werden in der Kontextangabe gebundene Variablen mit dem
passenden Typ eingeführt, die von den GF-Funktionen für self und result gebraucht
werden. GF schaut allerdings beim Aufbau des Verfeinerungsmenüs immer nur einen
Schritt weit in die Zukunft und weiß daher nicht, dass meistens für self und result gar nicht
alle Parameter auch ausgefüllt werden können. Daher tauchen diese beiden Funktionen
immer auf, wenn eine Instanz einer Klasse erwartet wird, auch wenn sie gar nicht passen.
10
3. Systemarchitektur
3.1. Überblick
Neben dem Editor und TogetherCC sind noch andere Komponenten daran beteiligt,
dem Benutzer das Verfassen von OCL-Einschränkungen zu ermöglichen. Die Grammatiken für die Klassen und Eigenschaften des UML-Modells müssen zuerst generiert werden. Dafür werden sie in einem Austauschformat an ein Haskellprogramm von Kristofer
Johanisson weitergeleitet, dass dieses dann erledigt. Wenn in der Klassendatei schon
OCL-Einschränkungen gespeichert waren, werden diese zusammen mit dem Austauschformat an einen ebenfalls von Kristofer Johanisson geschriebenen Zerteiler gegeben, der
im Erfolgsfall einen abstrakten Syntaxbaum zurückgibt. Gab es einen Zerteilfehler, so
wird ein OCL-Skelett verwendet, dass den OCL-Kontext angibt so dass der Benutzer
z. B. Methodenparameter als gebundene Variablen zur Verfügung hat.
Baum, generierte und die allgemeinen OCL-Grammatiken werden dann dem Editor und
GF, dass in einem zweiten Prozess läuft, übergeben. Der Benutzer kann nun mit dem
Editor OCL-Einschränkungen verfassen. Die Vorgehensweise dabei ist, wie im allgemeinen Fall auch, von oben nach unten.
Wenn das Programm beendet wird, kann die verfasste Einschränkung in der JavaKlassendatei gespeichert werden. Fertiges OCL wird direkt als OCL gespeichert. Hat
der Benutzer jedoch Fragezeichen offengelassen, so kann der verwendete Zerteiler das
entstandene OCL nicht einlesen. Daher wird in diesem Fall der abstrakte Syntaxbaum
von GF als Speicherformat gewählt.
3.2. Integrationsprobleme
Als diese Arbeit begonnen wurde, konnte die schon in TogetherCC integrierte Version
des Editors nicht mit den Grammatiken umgehen, die in [Dan03] erstellt wurden, da
der Grammatikgenerator mit dem neuen GF-Format für Module nicht umgehen konnte.
Dieser wurde erst auf einen gebrauchsfähigen Stand gebracht. Desweiteren war der Zerteiler noch nicht eingebunden, so dass dies ebenfalls zu Beginn dieser Arbeit gemacht
wurde. Ein von der neuen GF-Version und mit den erzeugten Grammatiken kompatibles
OCL-Skelett musste ebenfalls noch erzeugt werden können, was ebenfalls im Rahmen
dieser Arbeit umgesetzt wurde.
11
3. Systemarchitektur
12
4. Gebrauchstauglichkeitsverbesserungen
Wie die in Abschnitt 2 gefundenen Probleme gelöst wurden (oder zu lösen wären) ist im
folgenden skizziert. Die ausführliche Version ist in Abschnitt 9 zu finden.
4.1. Die nächste Verfeinerung
Das Verfeinerungsmenü war eine lange, unstrukturierte Liste mit teils kryptischen Namen. Um dem abzuhelfen, bekamen die Funktionen aussagekräftigere Namen und Beschreibungen, die in einem Hinweiskästchen (Tooltip) eingeblendet werden. Zuvor stand
der gleiche kryptische Name der Funktion auch in den eingeblendeten Hinweiskästchen,
so dass diese dem Benutzer keine Hilfe waren. Jetzt ist es dagegen möglich, dort einen
anderen, beschreibenden Text anzuzeigen.
Das Unüberschaubarkeitsproblem wurde durch eine Gliederung in Untermenüs wie Ope”
rationen für Sammlungen“ oder Boolsche Operationen“ gelöst. Jede Funktion wurde
”
dazu entsprechend markiert, so dass sie vom Editor richtig einsortiert werden kann. Die
Unterstützung für Untermenüs im Editor wurde in dieser Arbeit hinzugefügt.
Aber auch wenn der Benutzer Funktionen leichter finden kann und ihnen auch im Voraus
ansieht, was er mit ihnen ausdrücken kann, so zeigte ihm der Editor vorher jedoch nicht,
was gerade eigentlich von ihm erwartet wird. Bei manchen Funktionen wie einem oder“
”
ist das auch relativ einfach, aber besonders bei versteckten Typparametern hilft einem
auch die Darstellung in natürlicher Sprache nicht weiter, da sie eben nicht existiert.
Dieses Problem wurde dadurch gelöst, dass jeder einzelne Parameter einer Funktion eine
eigene Beschreibung bekam. Da der Benutzer immer Parameter von anderen Funktionen
ausfüllt, kann diese Parameterbeschreibung als Hinweis über dem Verfeinerungsmenü
angezeigt um dem Benutzer eine Hilfestellung zu geben für das, was der aktuelle Knoten
eigentlich soll.
Technische wurde dies mit Hilfe der sogenannten printnames“ von GF umgesetzt. Ei”
gentlich sind diese nur eine einfache Zeichenketten für GF, die jeweils einer Funktion zugeordnet sind und im Verfeinerungsmenü anstelle des Funktionsnamens angezeigt
wurden. Im Verlauf dieser Arbeit wurde ihnen jedoch eine Struktur verliehen, die vom
Editor verstanden werden kann. Ein Trennzeichen trennt so den Text, der im Verfeinerungsmenü angezeigt wird, vom Text für das Hinweiskästchen. Auch ein Etikett für das
zu benutzende Untermenü ist vorhanden.
13
4. Gebrauchstauglichkeitsverbesserungen
4.2. Unterstützung für HTML
Das die Darstellung in natürlicher Sprache sehr unübersichtlich werden kann, wurde
schon in [BJ05] beschrieben, und mit einem Formatierungssystem mittels u. A. HTML
gelöst. Allerdings konnte dieses nicht direkt verwendet werden. Das lag daran, dass
der Editor für die Zuordnung von Textschnippseln in der natürlichsprachlichen Darstellung und dem dazugehörigen Knoten im Syntaxbaum die Zeichenposition in der
XML-Zeichenkette, die GF dem Editor übergeben hat, verwendet. Eine genauere Beschreibung dafür findet sich in Abschnitt 10.1.6. Und in einer Beschreibungssprache kann
nicht direkt von der Länge eines Texts im Quelltext auf die Länge desselben Textes in
der Ausgabe geschlossen werden, was die Anzeige von HTML mit diesem Mechanismus
unmöglich gemacht hat.
Wenn dagegen die Länge des dargestellten Textes in der Ausgabe zum Kriterium wird, wo
ein Schnippsel anfängt und aufhört, und nicht die Position im HTML-Quelltext, so würde
das funktionieren. Dazu wurde die Java-Swing-eigene HTML-Anzeige-Komponente benutzt. Dadurch ist es möglich geworden, einen Teil von HTML (der alle in David Burkes
Grammatiken verwendeten HTML-Steuerelemente abdeckt) im Editor zu verwenden.
Und dies hat die Übersichtlichkeit in der Ausgabe drastisch verbessert.
4.3. Typumwandlungen
Wie in Abschnitt 2.9 erwähnt, sollte der Editor dem Benutzer das explizite Umwandeln
der Typen von Instanzen abnehmen. Folgendes wurde dazu umgesetzt:
Automatisches Einfügen Die GF-Funktion coerce, die für das Umwandeln zuständig
ist, wird automatisch eingefügt, wann immer dies sinnvoll ist. Diese Auswahl wurde im
Vorneherein getroffen und umfasst erst einmal alle Stellen, an denen auch eine Instanz
einer Klasse erwartet wird. Eine Einschränkung dazu sind Parameter, deren Typ von
einem versteckten Parameter abhängt. Dort kann der Benutzer oder GF direkt den Typ
der Unterklasse direkt wählen so dass keine Umwandlung nötig ist. Wenn dagegen ein
fester Typ wie z. B. Instance CollectionC erwartet wird, so ist immer eine Umwandlung
nötig, da sonst keine Untertypen passen würden.
Diese Auswahl erfolgt ebenfalls durch die Benutzung der printnames“. Ein Ausrufezei”
chen in der Parameterbeschreibung markiert den zugehörigen Parameter als Stelle, wo
der Editor automatisch ein coerce einfügt. Dies geschieht ohne Einwirkung des Benutzers.
Auch bekommt dieser diese zusätzlichen Funktionen im Normalfall nicht zu Gesicht, sie
bleiben verborgen. Nur falls der Benutzer an versteckten Typparametern dreht, kann es
passieren, dass zwei voneinander abhängende Funktionen unterschiedliche Typen haben.
GF erkennt solche Situationen und meldet diesen Fehler. Der Editor zeigt daraufhin alle
Typumwandlungen an um es dem Benutzer zu ermöglichen, den Fehler zu beheben.
14
4.4. Kleinere Verbesserungen
Löschen Funktionen unterhalb einer Typumwandlung müssen den gleichen Typ haben,
wie ein versteckter Typparameter der Umwandlungsfunktion selbst. Wenn jetzt beim
Löschen dieser unteren Funktion nicht auch noch das Typargument der coerce-Funktion
zurückgesetzt werden würde, ließe sich kein anderer Untertyp mehr auswählen. Daher
wird dieser Typparameter ebenfalls zurückgesetzt.
Umwickeln Damit eine Funktion f zwischen zwei anderen a und b eingefügt werden
kann, muss sie für a an die Stelle von b passen, also denselben Typ zurückgeben, und
zudem muss sie b als Parameter annehmen können. Vererbung hat hier keinen Platz, da
immer der gleiche Typ erwartet wird und GF keine Vorstellung von Untertypen hat.
Wie der Editor trotzdem dem Benutzer ein Verfeinerungsmenü bieten kann, dass diesem das Umwickeln auch mit Funktionen, die nur einen Untertyp des eigentlich von
a erwarteten zurückgeben oder die einen Obertyp von b als Parameter erwarten, ist
in Abschnitt 9.3.5 beschrieben. Umgesetzt wurde dieser rechenaufwändige Algorithmus
allerdings (noch) nicht.
4.4. Kleinere Verbesserungen
4.4.1. self und result
Um die überflüssigen self und result aus Abschnitt 4.4.1 im Verfeinerungsmenü loszuwerden, verfeinert der Editor im Hintergrund mit diesen und sieht nach, ob der Kontrollparameter ausgefüllt werden konnte. Ist dies der Fall, so werden self beziehungsweise result
im Verfeinerungsmenü belassen, andernfalls werden sie daraus entfernt.
4.4.2. Einfachere Erreichbarkeit von Eigenschaften von self
Wenn der Benutzer Aussagen über eine Klasse machen will, dann wird er nicht umhinkommen, über deren Attribute zu reden. Diese waren jedoch mehrere Verfeinerungen
entfernt, etwas was relativ umständlich ist, was auch von Leuten bemerkt wurde, denen
der Editor gezeigt wurde.
Daher führt der Editor jetzt im Hintergrund diese Verfeinerungsschritte aus und stellt
sie dem Benutzer in einem Schritt zusammengefasst im Verfeinerungsmenü zur Auswahl.
Dadurch wurde dieser häufige Arbeitsvorgang leichter ersichtlich und stark beschleunigt.
4.4.3. Wegkommen von verstecken Parametern
Wann immer ein Knoten im abstrakten Syntaxbaum ausgewählt wurde, der nicht in der
Textdarstellung auftaucht, wie es z. B. bei verstecken Typparametern der Fall ist, konnte
15
4. Gebrauchstauglichkeitsverbesserungen
die Textdarstellung nicht mehr zur Auswahl anderer Knoten ausgewählt werden. Klicks
in diese wurden ignoriert.
Dieses Manko ist nun behoben. Die Auswahl von Baumknoten funktioniert jetzt immer,
egal der momentan ausgewählte Knoten eine sichtbare Darstellung im Text hat oder
nicht.
4.4.4. Geänderte Auswahlfarbe
Um eine Fehlersituation, wenn voneinander abhängende Knoten nicht den gleichen Typ
haben, anzuzeigen, werden die entsprechenden Teile in der Textdarstellung rot markiert.
Rot ist eine Signalfarbe für Fehler, so dass der Benutzer direkt merkt, dass etwas nicht
stimmt. Allerdings hat der Editor Grün als Auswahlfarbe verwendet. Und gerade im
Kontrast mit Rot drückt dies aus, dass etwas besonders als richtig“ markiert werden
”
soll. Aber dass Zusammenhänge zwischen Knoten nicht verletzt sind, sollte keine besondere Situation sein, sondern der Normalfall. Und der Normalfall sollte keine zusätzliche
Aufmerksamkeit des Benutzers auf sich ziehen, genauso wie die Lampen des Armaturenbretts im Auto nur leuchten, wenn ein mögliches Problem vorliegt, aber ansonsten
aus sind.
Daher wird jetzt die Auswahlfarbe verwendet, die für Swing als Standard eingestellt ist.
Eine Auswahl ist eben nur das und keine Markierung als richtig“.
”
4.4.5. Zerteilfenster per Mittelklick
Ein Mittelklick in die Textdarstellung öffnet ein Fenster, in dem man den Text für die
Verfeinerung des aktuellen Knotens eingeben kann. GF versucht dann, diesen zu zerteilen und mit den entsprechenden Funktionen zu verfeinern. Allerdings wurde früher bei
schon verfeinerten Knoten der Text in der zuletzt von GF gesendeten Sprache angezeigt.
Und wenn man diese nicht beherrscht, kann man eben keine kleinen Änderungen daran vornehmen. Was dem Geiste des mehrsprachigen Verfassens, bei dem man nur eine
Sprache kennen muss, um in allen Veränderungen bewirken zu können, widerspricht.
Daher wird jetzt diejenige Sprache ausgewählt, in deren Textdarstellung man geklickt
hat. Man kann also eine Sprache auswählen, die man beherrscht und ist nicht der Willkür
von GF unterworfen.
Für OCL kann dieses Fenster jedoch nicht benutzt werden, da der allgemeine GFZerteiler OCL nicht zerteilen kann. Diese Fähigkeit ist mehr für andere Grammatiken
gedacht, die ebenfalls mit dem Editor benutzt werden können.
16
4.4. Kleinere Verbesserungen
4.4.6. Sicherheitsabfrage beim Beenden, ob man speichern will
Wenn der Benutzer früher den Editor beendet hat, wurde dies sofort ausgeführt. Ungesicherte Arbeit ging dabei verloren.
Praktisch jedes andere Programm blendet jedoch eine Sicherheitsabfrage ein. Folglich
kann der Benutzer dieses auch vom Editor erwarten. Daher wurde eine solche Abfrage
eingefügt.
17
4. Gebrauchstauglichkeitsverbesserungen
18
5. Fazit
Die meisten der groben Bedienprobleme aus Abschnitt 7 sind in dieser Arbeit beseitigt
worden. Erst jetzt kann der Editor auf echte“ Benutzer in einem Gebrauchstauglich”
keitsexperiment losgelassen werden, um zu sehen, ob der grammatikbasiere Ansatz für
OCL überhaupt funktioniert. Der Preis dafür, dass keine OCL-Kenntnisse mehr verlangt
werden, ist ein völlig anderes Bedienkonzept. Kein Eintippen der einzelnen Teilformeln
mehr, sondern ein von oben nach unten durch Operator-Auswahl Verfeinern ohne am
Anfang schon die Operanden auswählen zu können, wie man es von anderen Editoren
gewöhnt ist, wo man sie einfach hinschreiben kann.
Dies widerspricht [KU93], wo empfohlen wird, dass ein grammatikbasierter Editor auf
erfahrene Benutzer und nicht auf Anfänger zuzuschneidern ist, da eine gewisse Einarbeitung unumgänglich sein wird. In dieser Arbeit wird einem Benutzer allerdings die
Einarbeitung in OCL abgenommen, so dass sich immer noch ein Netto-Vorteil ergeben
kann. Aber ob das wirklich der Fall ist, muss noch untersucht werden.
5.1. Nur ein Editor speziell für OCL?
Das ursprüngliche Ziel war es, den allgemeinen GF-Editor auf OCL zuzuschneidern.
Allerdings waren einige der gefundenen Probleme allgemeinerer Natur und nicht OCLspezifisch. Dies waren Informationsdarstellungsprobleme, oder genauer was eine bestimmte GF-Funktion macht, das unübersichtliche und zu lange Verfeinerungsmenü,
welche Rolle der aktuelle Knoten im Syntaxbaum spielt und die Darstellung der Ausgabe ohne Struktur und Formatierung.
Eine allgemeine Lösung für diese Probleme würde es für OCL nur noch nötig machen, die
entsprechenden Erklärungstexte bzw. Formatierungsregeln zu schreiben, was in dieser
Arbeit auch getan wurde. Für HTML war dies sogar nicht mehr nötig, da David Burke
dies schon in den Grammatiken umgesetzt hatte.
Auch die Eingabe von Zahlen und Zeichenketten wurde deutlich einfacher gemacht. Eine
einfache Sicherheitsabfrage beim Beenden und die Sprachauswahl beim Zerteileraufruf
per Mittelklick kommen dem allgemeinen Editor zu Gute. Und die durchgeführten Verbesserungen am Quellcode des Editors sollten auch nicht vergessen werden.
Diese auch für allgemeine GF-Grammatiken nutzbaren Erweiterungen/Verbesserungen
führten dazu, dass der Editor zusammen mit GF 2.3 veröffentlicht wurde.
19
5. Fazit
5.2. Ausblick
Eine Reihe von Dingen wurde in dieser Arbeit nicht angegangen, hätten allerdings ebenfalls in ihr Thema gepasst und wären nächste Arbeitsschritte:
Pseudo-Elemente des UML-Modells OCL besitzt einen Mechanismus um Kurzschreibweisen für längere Ausdrücke einzuführen. Dieser wird in den Grammatiken etwas stiefmütterlich behandelt. Der Grund liegt unter anderem darin, dass die Semantik dieser
Pseudo-Modellelemente sich von OCL-Standard zu OCL-Standard geändert hat und
dies für OCL 2.0 wieder ansteht. In diesem Editor wurden zu erst die anfänglichen Bedienhindernisse angegangen. Und diese Kurzschreibweisen werden vermutlich erst von
fortgeschrittenen Benutzern verwendet, so dass es in den Augen des Autors gerechtfertigt
war, deren Vereinfachung zurückzustellen.
Drag & Drop Wenn ein Knoten des Syntaxbaumes an anderer Stelle wiederverwendet oder dahin verschoben werden soll, so wäre es wünschenswert, dies den Benutzer
mittels Drag&Drop zu ermöglichen. Auch könnte das Ziehen eines Wortes aus der TextDarstellung auf ein Fragezeichen ein Verschieben oder Kopieren bewirken.
Händische Linearisierungsregeln Besonders für Deutsch ist die Darstellung von Elementen des UML-Modells in der natürlichen Sprache nicht sehr gelungen. Dieses Problem besteht schon seit [Dan03], lässt sich aber nur im Zusammenspiel von Benutzer,
TogetherCC und dem Grammatikgenerator von Kristofer Johanisson lösen. Wie eine
meistens mit englischen Begriffen bezeichnete Klasse im Deutschen heißen soll, lässt sich
nur schwer automatisch herausfinden und wäre zudem ein ganz anderes Thema. Es dem
Benutzer zu ermöglichen, Klassen und Eigenschaften des UML-Modells händisch Linearisierungsregeln zuzufügen, die dann vom Grammatikgenerator aufgegriffen werden,
wäre hier ein Schritt hin zu lesbarerem Deutsch.
Zerteilen Eine Fähigkeit, die von Personen, denen der Editor gezeigt wurde, nachgefragt wurde, ist das Zerteilen von Teilausdrücken. Was allerdings weder vom allgemeinen
GF-Zerteiler, noch vom OCL-Zerteiler von Kristofer Johanisson, unterstützt wird. Dieser
müsste dazu angepasst werden.
Speichern währen dem Editieren Der Editor bietet Knöpfe zum Speichern an. Diese speichern allerdings in eigenen Dateien und nicht im Java-Quelltext, so dass sie
beim nächsten Laden nicht automatisch beachtet werden. Hier sollte dem Benutzer die
Möglichkeit gegeben werden, den momentanen abstrakten Syntaxbaum als JavaDocKommentar zu speichern, so dass er bei einem unbeabsichtigen Beenden des Programmes
wiederhergestellt werden kann. Dies wird momentan leider noch nicht unterstützt.
20
5.2. Ausblick
Geschwindigkeit Der Editor kann zum Starten zwischen 40 und 75 Sekunden brauchen. Das ist unangenehm viel, gerade wenn man für mehrere Methoden/Klassen Einschränkungen schreiben will und man den Editor dafür immer wieder neu laden muss.
Solange das Modell selbst nicht geändert wurde, könnte GF mit den geladenen Grammatiken im Hintergrund belassen werden und ihm bei Bedarf nur ein neuer OCL-Kontext
übergeben werden. Dadurch müssten die Grammatiken nur einmal eingelesen werden,
was viel Zeit sparen kann.
Drastisch (auf ca. 15 Sekunden) lassen sich die Ladezeiten reduzieren, wenn auf das Laden der deutschen Grammatiken verzichtet wird. Auch der Speicherverbrauch von GF
sinkt dadurch auf ca. 60 MB ab.
Mit einer HTML-Komponente, die das Anhängen von HTML an ein bereits geladenes
Dokument unterstützt, würde das Berechnen der Indices der Text-Schnippsel von GF
nicht mehr quadratisch sein wie bislang, wo das HTML-Dokument immer länger wird,
aber jedes Mal neu geladen werden muss. Die bisherige Herangehensweise führt dazu,
dass der Editor bei längeren Einschränkungen etwas an Geschwindigkeit verliert.
Dass der Editor im Hintergrund bei GF nachfragt bzw. automatisch verfeinert um dem
Benutzer Arbeit abzunehmen kostet Zeit. Das ist allerdings prinzipbedingt, da der Editor
ursprünglich nur als reine Anzeigekomponente gedacht war, die keine Eigenintelligenz
besitzt.
Eine Geschwindigkeits- oder Speicheroptimierung ist in dieser Arbeit allerdings nicht angegangen worden, da erst die Funktionalität bereitgestellt werden sollte. Sollte der syntaxgeleitete Ansatz das Erstellen von OCL-Einschränkungen wirklich erleichtern, dann
kann die Geschwindigkeit des Editors immer noch angegangen werden.
Andere Spezifikationssprachen Die meisten Verbesserungen, die im Rahmen dieser
Arbeit umgesetzt wurden, sind nicht OCL-spezifisch. Selbst diejenigen, die nicht für
allgemeine GF-Grammatiken eingesetzt werden können, wie z. B. Typumwandlungen
sind nicht wirklich OCL spezifisch, sondern bilden nur ein Typsystem mit Untertypen
nach, dass auch in anderen Spezifikationssprachen wie JML existiert. Daher wären nur
minimale Änderungen am Editor selbst nötig, wenn die zugehörigen GF-Typen nach
dem selben Schema benannt werden würden.
Eine konkrete Grammatik für JML für die abstrakte Grammatik für OCL würde gar
sofort mit dem Editor funktionieren. Dafür wäre das Schreiben dieser Grammatik sehr
aufwändig, da die Elemente der beiden Sprachen nicht eins zu eins auf einander abbildbar
sind.
21
5. Fazit
22
Teil II.
Multilingual Syntax Editing for
Software Specifications
— Englische Ausarbeitung —
23
6. Introduction
The area of this work is software specification. Saying, that a given program is correct,
only makes sense, if it is known, what the program ought to do. And especially in
the context of formal methods, it is not enough, if just a human knows, what the
program should do. This knowledge has to be formalised. For that, specific formalisation
languages are used. One of them is OCL1 . But the language mostly used in practice
for specifying, is natural language, while the use of formal languages is not wide-spread.
The underlying work of this thesis intends to bridge the gap between natural language
used in reality and a formal language (in this case OCL) which is needed for formal
methods.
First in this section the tools that this work taps on are described. Later, a motivation
on why this work is necessaray is given and the topic is explained.
6.1. The Grammatical Framework
6.1.1. Overview
GF2 is a framework for writing and using grammars. It is not just a grammar formalism,
as BNF is one, but also an implementation of that formalism, that, among others, offers
generating, parsing and type checking. It is meant to model formal and (well defined
subsets of) natural language. Not only does it take care of the syntax of a language, but
also semantics, especially with the dependent types of Martin Löf’s type theory.
The main feature of GF is separation of the abstract syntax tree (AST) from its representation in concrete languages. In a so-called abstract grammar module the categories,
possible return types of functions, and those functions with their signature are defined.
But nothing is said about how such a category or fun should look like, when it is linearised, as displaying the tree in a concrete language is called. That is done in one or
more concrete grammar modules. Here, for every cat, a lincat or linearisation category
is defined, that determines, how the cat should look like in that language. Likewise, for
every fun there is a lin, or linearisation rule, that defines the concrete representation
of that function.
There can be more than one concrete grammar module for an abstract one at the same
1
2
http://www.omg.org/cgi-bin/doc?formal/03-03-13
http://www.cs.chalmers.se/~aarne/GF/
25
6. Introduction
time in GF. That way, the AST, which all loaded concrete grammars have in common,
can be linearised into all those languages at the same time. If GF parses a string from
one of those languages into an AST, it will be linearised into the other languages which
results in translation. An example of an AST and the linearisation in several languages
can be seen in figure 6.5.
As concrete languages formal and informal languages are possible. For ten natural languages3 the resource grammars4 exist. They contain linguistic knowledge like inflection
and word order and offer a common (but not completely identical) interface to them for
the application grammar writer, to relieve him from that part of his work and let him
concentrate on what to express, not how to inflect it. For formal languages that is not
necessary, because they, as their name tells it, are already completely formalised. Nevertheless resource modules for precedence rules could be shared between several formal
languages, but such an undertaking has not yet been done.
In this work, the languages used are the formal language OCL and the natural languages
English and German.
GF is described in more detail in [Ran04].
6.1.2. Example
As an example, two funs from the OCL grammars will be explained. In the abstract
syntax they are defined with their cats as follows:
cat
cat
cat
fun
fun
Sent ;
Instance ;
Class ;
orS : Sent -> Sent -> Sent ;
eq : ( c : Class ) -> (a , b : Instance c ) -> Sent ;
First come the declarations of the categories, that are used in this example. They
introduce these types, but says nothing about how they look since this is the abstract
syntax, not a concrete one. The keyword fun begins a judgement for a fun, which is
followed by the name and, separated by a colon, the type signature of the fun. The
signature consists of first the argument types and at the end the category or return
type of the fun. For both funs above Sent would be the return type. For the arguments,
they differ. orS just takes two Sent arguments which can also be seen in figure 6.1. These
can be filled in and they will show up in the concrete linearisation, but nothing special
happens here.
For eq, things are a bit more complicated. Here dependent types come into play. To
specify, which type depends on which, they are given names. Here, the type of the second
and third argument depends on the value of the first argument. When c is refined to
IntegerC, then a and b both have to have the type Instance IntegerC. In this context,
3
4
Danish, English, Finnish, French, German, Italian, Norwegian, Russian, Spanish and Swedish
http://www.cs.chalmers.se/~aarne/GF/lib/resource/
26
6.1. The Grammatical Framework
Figure 6.1.: The AST and linearisation of orS in the graphical editor for GF
(a,b : Instance c) has the same meaning as (a : Instance c)-> (b : Instance c).
When c is not yet determined, as visible in figure 6.2(a), not much is said about the
type of a and b. But when c is refined (see figure 6.2(b)), both are fixed to one type, in
the example Instance IntegerC.
Having the two Instance arguments depending on the first argument makes it possible to
enforce that both have to have the same type, something which is needed, since in OCL
only comparison between objects of the same type are allowed. GF will automatically
fill in c, when one of the Instance arguments is refined first, since there is a GF constraint
or dependency, that locks the types of c and a/b together. GF will notice it and report
it, if one deletes c afterwards and refines b with a different type than a. When two types
depend on each other, but do not match, it is called a GF constraint. GF will attach
this constraint to the nodes and the editor will colour the linearisation of these nodes
red.
Also the return type of a fun may depend on one of the arguments. Note, that an
Instance IntegerC and Instance StringC are different types on the abstract syntax level.
Only on the concrete syntax level do they share the same linearisation rule, the one for
Instance.
Now for the linearisation rules for these two functions for OCL. Note, that these rules
are simplified and, for example, lack support for operator precedence.
27
6. Introduction
(a) no class chosen
(b) Integer chosen as
the class to compare on
Figure 6.2.: The parametrised equality operator eq. On the left with open Class argument, where the exact type of the Instance arguments is not yet determined
(the type is printed as Instance (?)). On the right, IntegerC has been chosen as the class. Now the type of the two other arguments is known to be
Instance IntegerC.
l i n c a t Sent = { s : String };
l i n c a t Class = { s : String };
l i n c a t Instance = { s : String };
l i n orS sent1 sent2 = { s = sent1 . s ++ " or " ++ sent2 . s };
l i n eq c a b = { s = a . s ++ " = " ++ b . s };
Both are pretty straightforward. Every linearisation of a fun is a record. The layout of
this record is defined in the lincat judgement. In this example, the record just contains
a string field s since there exists no such thing as inflection in OCL. The lin judgements
assign the record fields their values. Linearisation is recursive and starts in the leaves
and the funs above can access the linearisation records of their arguments, but not their
tree structure. This limitation/feature is called compositionality.
For both lins, in the beginning the s field of the first argument is taken, then or /= is
appended and the s field of the second argument is printed afterwards. Note, that the
class argument of eq is dropped in the linearisation. Therefore it is called a hidden type
argument. These hidden type arguments occur quite often in the OCL GF grammars
and will be discussed later on.
Simplified English lins, that don’t use the resource grammars, could look like below.
l i n c a t Sent = { s : String };
l i n c a t Class = { s : String , modelName : String };
l i n c a t Instance = { s : String , modelName : String };
l i n orS sent1 sent2 = { s = sent1 . s ++ " or " ++ sent2 . s };
l i n eq c a b = { s = a . s ++ " is equal to " ++ b . s };
Not much of a difference here in this simplified version. Classes are normally referred to
by a natural language name, which would be pay card for PayCard for example like in
the pay card in the linearisation of self (see section 7.11 for more about that fun), but
in the context declaration the real, model name is used to make the mapping clear (as
visible in figure 7.1). In the example funs above that field is not used, though.
The ‘real’ lins use the resource grammars, but their use was not part of this work, so
28
6.1. The Grammatical Framework
their use is not shown here.
6.1.3. The Java Editor for GF
GF features a graphical syntax editor. In the editor, all operations operate on the AST,
nothing on the linearisation, whereas the AST will be linearised in all loaded languages.
That way, all operations have their effect on all languages at the same time. Thus,
multilingual editing is possible. One does not have to know all the languages to produce
a text in them, it is enough to know one of them. The linearisation in that language can
serve as a proof linearisation to check, if the editing commands produced the wanted
result. The linearisation in the other languages will always have the same meaning
too, so they do not need to be checked (given that the writers or the foreign language
grammars did a good job).
Editing in the editor is top-down. To start, one selects the type of the root node
(Sentence for example, as in figure 6.3), refines that and then is led to the child nodes
(which for hunting, as in figure 6.4, are subject and object) and so on.
In figure 6.3 you can see the main window of the editor. It is divided into three big
parts.
In the upper left, one can see the abstract syntax tree. Here you can see which fun is
where and takes which arguments. After each fun you can see the type of it. When
one clicks in the tree, GF is told to set that node as the active node. It then sends the
updated state back for display, which accordingly reflects the state change.
In the upper right there is the linearisation area. Here the linearisations in the selected
languages (in the figure Finnish and English) are displayed. If wanted, one can also show
the tree in a textual representation there, but that is of limited use and thus hidden by
default. If the currently active node is visible in the linearisation (for example, type
arguments normally are not), the part in the linearisation that belongs to the active
node, is coloured green. To switch to another part of the text one can just click there
in the linearisation area.
In the bottom there is the refinement menu. There are several kinds of commands.
The most important one is r, short for refine. refine is offered if, like in figure 6.3,
an open (or unrefined) node, a so-called metavariable, is selected. With refine one can
apply a fun at the current node, so that it will appear in the AST at the position of that
metavariable. Once refined, GF will jump to the next metavariable, shown in figure 6.4.
If a refined node is selected, there are several possibilities, as visible in figure 6.5. One can
delete the fun with all its children there (the whole subtree), in the example resulting
in figure 6.6. Or change the currently selected fun into another one, as done in 6.7.
Furthermore it is possible to insert a fun between the current node and its parent (only
if return type of the wrapper fun and the type of the chosen argument are the same as
the one of the current node, see section 9.3.5 for more about that), called wrapping in
GF, which basically is bottom-up editing. Its effect is shown in figure 6.8 Alternatively,
29
6. Introduction
one can just remove the current node with ph (peel head) and use one of its arguments
instead, as in figure 6.9, which does not delete the whole subtree like delete does. Here
also the type of the fun and the selected argument have to be the same since this
command is the reverse of wrapping.
There is also a clipboard, in which one can put arbitrarily many subtrees with ac, add
to clipboard. With rc, refine from clipboard, one can paste them again, given that they
have a type suiting type for the current node.
A full description can be found in [Khe03].
Figure 6.3.: The main window if the Java GUI for GF. Currently, an unrefined node is
selected, so only refine commands are offered.
6.2. KeY
This work is part of the KeY system ([ABB+ 05]). ‘The aim of the project is to integrate
formal software specification and verification into the industrial software engineering
processes’5 . It is believed in the project, that a verification tool outside the normal
development environment in software engineering practice will not get wide-spread use.
It has to be accessible within arm’s reach of the developer. For that it must not use
esoteric languages, but a language the developer already uses. Therefore, JavaCard6 has
5
6
taken from http://www.key-project.org
http://java.sun.com/products/javacard/
30
6.2. KeY
Figure 6.4.: The first refinement has been executed, and updated tree and linearisation
are shown. The next node has automatically been selected by GF.
been chosen as the target language.
Specification occurs on a higher level, not directly on the implementation, but on the
model level. So the model has to be formalised in some way to be accessible by the
specification. The most often used modelling language is the Unified Modelling Language
UML. But UML alone does not say much about the semantics of a model besides state
machines, model structure, caller-callee relations and cardinalities. Return values, for
example, are not covered at all.
In use cases, much of the specification is attached as informal text. But informal text
with all its ambiguities cannot be used as a formal specification when it comes to formal
verification. So the model has to be enriched with formal specifications.
One way to do this is using the Object Constraint Language, in short OCL. With
this specification language class invariants and method contracts consisting of pre- and
postconditions can be stated formally and precisely.
But knowledge and use of OCL is not wide-spread. Therefore, KeY not only tackles
proving that a programme behaves as specified, but also assists in specifying. One
approach are specification patterns as described in [Bub02]. Another one is the basis for
this work and described in [Johar], but also in this work.
To be in arm’s reach of the developer, specification and verification must be integrated
31
6. Introduction
Figure 6.5.: An already refined node has been selected. Now one can either delete the
current subtree, change the current fun into another one or wrap the current
subtree with another fun.
Figure 6.6.: Tree and linearisation after sending the delete command d in figure 6.5 to
GF.
Figure 6.7.: Tree and linearisation after sending the change command ch Thick in figure
6.5 to GF.
Figure 6.8.: Tree and linearisation after sending the wrap command w Dirty in figure
6.5 to GF.
32
6.3. ‘Formal and Informal Software Specification’
Figure 6.9.: Tree and linearisation after sending the peel head command ph in figure 6.5
to GF.
into the used UML and development tools. Together Control Center7 is such a tool,
and KeY has been integrated as a plug-in into TogetherCC. In figure 6.10 one can see,
how KeY is callable from within TogetherCC. Specifying and verifying are offered in the
context menu of a class/method and OCL constraints are saved together with the class
itself (see section 8), so no external tools have to be used.
6.3. ‘Formal and Informal Software Specification’
The direct context of this work is the Phd thesis of Kristofer Johanisson [Joh05]. An
overview of it is given here.
In [HJR02] (part of [Joh05]), a system is presented, that bridges the formal language
OCL and natural language. Using GF as the grammar formalism, Kristofer Johanisson
wrote a common abstract grammar for both OCL and English, and also concrete grammars for these languages. As GF offers multilingual editing with the generic Java GUI
for GF, one could then create OCL constraints without knowing OCL. In [Dan03], a German concrete grammar for the abstract OCL grammar was written, using the resource
grammars. The author later on translated this into English. David Burke improved the
natural language rendering in [BJ05] (also part of [Joh05]) and introduced a system for
formatting. After that, the author adapted these improvements for the German grammars, so that both English and German benefitted from them. In [Joh04] (also included
in [Joh05]), Kristofer Johanisson describes an OCL parser he wrote. Before that, due
to limitations in the generic GF parser, OCL couldn’t get parsed, so only multilingual
syntax editing was possible by then. Now also translating OCL into natural language
was possible.
However, besides integration problems (see section 8.2), the main problem was, that the
generic Java GUI together with the abstract OCL grammar, was not very usable, but
instead quite clumsy to use.
7
http://www.borland.com/us/products/together/index.html
33
6. Introduction
Figure 6.10.: Creating the specification and the verification that the implementation
adheres to it with KeY can be called directly from the context menu of
TogetherCC.
34
6.4. Goals
6.4. Goals
And to remedy this situation is the aim of this work. The idea of this system is to make
OCL editing possible for someone, who does not know OCL. The first step for that is to
identify the main problems that a user of the editor would have. Then to devise ideas
to overcome these problems and finally, to implement these ideas.
The basic working principle is to offer the user only everything, that is possible at the
current situation. On the one hand, that restricts the user, because it’s not possible
to enter something right away, but on the other hand, this ‘railing’ has its advantages.
This approach prevents syntax errors, since creating the written OCL is done by GF,
not by the user, and GF only allows well-formed OCL. Besides that, the user does not
have to know all OCL operations by heart, but just needs to select from a list, from
which all non-applicable operations already have been removed. Or, taken further, the
user does not have to need to know, what OCL operations exist, the system should tell
and explain that to him. And recognizing something presented as the thing needed is
much easier than having to remember it.
This strength, allowing OCL editing by point&click, should be built upon and increased.
Stumbling blocks and unnecessary editing steps, however, should be removed. In short,
the editor should get usable.
6.5. Potential use and users
The editor is not meant for OCL experts. They already know, how to express things in
OCL and how the syntax is. They know the type concept of OCL and don’t need to
be restricted to always write something type-correct. For example, they can start with
a > aStringSet (which would not be possible in the editor), but by continuing as in
a > aStringSet->any(s : String | s.substring(0,1)= "A").size() this gets correct
again.
For people who do not know OCL (yet), but only the domain, this restriction is not so
limiting. They can’t enjoy their freedom yet, but need to be taken by the hand. And this
is something the editor does/should do. When they have fledged and want to write OCL
directly, they still have benefitted from it. That makes the editor to an introductory
tool. It helps beginners to write and learn OCL. The latter only, if the user wants to.
He does not need to look at the OCL, just looking at the English or German version is
enough, since the OCL will have the same meaning.
Teaching is another potential area of application. Here formalisation and OCL can be
tought at the same time, since the latter is not a prerequisite for the former.
35
6. Introduction
36
7. Usability Problems of the Previous
Editor
No real usability study has been done here. The author found too many blocking flaws at
the beginning of the work, which would easily overshadow other, still hidden, problems.
These blocking problems, which should be solved before such a study, are listed below.
This list does not claim to be complete, experiments with untrained subjects will surely
reveal more problems, especially when these problems are resolved and give way to a
less restricted use of the editor, where problems lying deeper can be discovered.
The problems in this chapter are described from a user’s point of view and have different
reasons. These reasons will be discussed and explained too, since shedding light on the
source of a problem is the first step to solve it.
7.1. Which function does what?
When using the old editor started from TogetherCC, one was greeted with a huge list of
strange names like in figure 7.1. These are the names of the funs in the abstract syntax
of GF.
These names were sometimes understandable, like intEq for example. For others like
one, one can’t even guess, what they would do. If a node of a different type than Sent
is selected, the situation is similar.
Not recognising what a function stands for is one thing. Understanding what it could be
used for is another. For the more complicated OCL functions like iterate or anyOclAsType
even having the name they have in OCL does not help, if you don’t know OCL. The
point of the editor is to help people, who do not know OCL, to author OCL constraints.
For that, the editor should help the user choosing the right fun. At least it should
explain, what the functions can be used for. Just telling, which functions are applicable,
is not enough.
7.2. Not seeing the forest because of so many trees
Let’s take a node of type Sent, standing for sentence. There are several different kinds of
functions that return this type. Some are boolean functions to conjoin other sentences,
37
7. Usability Problems of the Previous Editor
Figure 7.1.: Screen of the editor directly after starting. Note, that the screenshot has
been altered, so that the whole refinement menu is visible at once.
like orS or andS. Other functions compare objects like the equalities mentioned in section
7.9. Again others deal with the OCL collection types and make propositions about them
like one or contains. So functions from different areas are mixed here.
But when one wants to and-conjoin two sentences, one does not want to search these
functions between all those collection and other functions like comparisons. A list with
over 50 entries to choose from is just too long. Some reduction is needed here.
7.3. What am I to do?
The editor always only offers refinements that are type-correct. But the type alone is
not enough for the user. Which purpose the current refinement serves semantically is
not said by the editor and can stay unclear. For example, there are hidden (with regard
to the linearisation) type arguments (as in the example in section 6.1.2). An example
for that is eq, the parametrised equality, as seen in figure 7.2. Its first argument selects
the type on which the comparison is done. But that is not explained anywhere, the user
is out to guess.
On a side node: It is not possible to click in the linearisation to escape such a hidden
argument. If it has been selected in the tree, the linearisation area becomes unclickable.
One can refine the current node, which will make GF select the next one, but selecting a
part of the linearisation to select any other node does not work. One has to use the tree
38
7.4. Where does what begin?
Figure 7.2.: The hidden argument of eq is selected. But what does it do?
to select one. Normally, one can select the wanted node by clicking in the linearisation,
so this does not conform to the user’s expectancy without other benefits. It’s just an
annoying limitation.
7.4. Where does what begin?
If an OCL constraints becomes longer, it is hard not to lose the overview, as mentioned
in [BJ05] or visible in figure 7.3.
An easy, but yet very effective way to help here is formatting (see [BJ05] for details).
In the underlying work of David Burke such formatting is implemented for the English
GF grammar for OCL. But the editor does not support formatting mark-up in the
linearisation. GF just sends text, which is displayed character by character.
Writing formatting for some improvements, that didn’t change the indices of the displayed characters with respect to their position in the text from GF like line-breaks or
nicer bullets, helped a bit, but to implement nice indentation in GF would be really,
really messy since a linearisation of a fun normally stands on its own and does not
depend on its context. And it would not be implementable with the system introduced
by David Burke. This problem is discussed in more detail in section 10.1.6.
39
7. Usability Problems of the Previous Editor
Figure 7.3.: A non-trivial OCL constraint of the PayCard example. Except for (*) for
bullets, but even they are not followed by line-breaks, no structure is given
to make the constraint more concise.
7.5. But my Integer is a Real, isn’t it?
When comparing Reals for example, only funs of type Real are listed in the refinement
menu, but no Integer funs. In general, only funs of the exact type are offered there,
never funs for a non-reflexive subtype.
The reason for that is a fundamental one. Since OCL is part of UML, which got most
famous with its inheritance-charts, OCL’s type system features subtyping. But the
type system of GF does not do that. So the OCL editor has to work around this, in
this context, deficiency of GF. This is done by using dependent types, one of the main
features of GF’s type system (see [Ran04]).
The main element in this emulation is the fun coerce:
f u n coerce : ( sub , super : Class ) -> Subtype sub super
-> Instance sub -> Instance super ;
This fun does an explicit upcast. The first argument, sub, selects the subclass, while
super selects the superclass. The object, that is to be upcast, is the fourth argument and
has the type Instance sub. That means, that this type depends on the first argument,
sub (see the example in section 6.1.2 for more about dependent types). The return type
40
7.5. But my Integer is a Real, isn’t it?
of coerce is an example for a depending return type. This type is determined by the
second argument super. Whenever a fixed type like Instance IntegerC is expected as an
argument, and a coerce used to refine it, GF will automatically refine super also, since
it is bound to the return type fixed in this situation, and cannot be chosen freely.
So far, this allows one, for every pair of classes sub and super, to use an Instance sub as
an Instance super. But that would allow type-incorrect OCL, and that is not wanted,
since the purpose of this editor is to relieve the user from thinking about syntax and
type correctness. To prevent incorrect subtyping there is the third argument. Here a
so-called subtyping witness (see below) has to be inserted by hand.
For beginners, coerce is easy to overlook, since in UML one does not have to think of
explicit upcasts, so one would expect the variables and functions, that return Instance
sub to appear in the refinement menu for an Instance super. But they don’t.
Even if one knows, what coerce does and what its arguments are (see above), it is still
cumbersome to introduce it everywhere by hand, since it is an additional step that
doesn’t match the UML model, and probably not the user’s mental model either. Every
object of class A is already also an object of class B if A B. As virtually superfluous
steps slow down the user unnecessarily, it would be better to abolish them.
Another disadvantage of many coercions is the cluttered tree. coerce with its 4 arguments
takes up quite some space in the tree view, which distracts from those OCL functions,
that do not just compensate deficiencies.
Subtyping Witnesses For direct subtyping relation B A, there exists a subtyping
witness Bconforms2C : Subtype B A in the grammars. However, for transitive suptyping
like C B A, one has to use
subTypeTrans : (c , b , a : Class ) -> Subtype c b -> Subtype b a
-> Subtype c a ;
which allows one to built the wanted subtyping witness of type Subtype c a. For a
reflexive coercion from A to A,
subTypeRefl : ( c : Class ) -> Subtype c c ;
has to be used, which returns such a reflexive subtyping witness.
Building transitive subtyping witnesses by hand is unnecessarily complicated. For a
situation like D C B A, figure 7.4 shows the AST for a coercion from D to A.
And to construct something like that should not be imposed on the user.
Creating additional witnesses for transitive subtype relations in the grammars themselves
would automatically increase the grammar size, in the worst case quadratically. This is
the case when there is just one long inheritance line, where each new class at the bottom
needs a subtyping witness to all classes above plus to itself. But that should be very
rare, and if the classes are not empty, but have a number of properties, the added funs
shouldn’t have such a big size impact.
41
7. Usability Problems of the Previous Editor
Figure 7.4.: Given D C B A, this is how a D can be used as a A. The rule
for transitive subtyping only covers one additional step, so after the first
subTypeTrans we still have to prove that B D, which is done in the second
subTypeTrans.
The linearisation record of such a subtyping witness consists of just one string, so they
don’t take up that much memory compared to, for example, classes, which as common
noun phrases (CN) contain a table of all cases and numbers. And reducing the burden
on the user should allow for a slight increase in grammar size (and thus, of loading time
and memory consumption).
7.6. I want to enter 42.
When one wants to enter an arbitrary integer number, one only sees 0,1 and 2 which are
directly offered. There is no visible way to go further. With descriptions of the menu
entries, one could find out that the fun int takes an Int as its argument, which is the
built-in type for integer numbers in GF and can be any integer number. But if this fun
is selected, just a list of 6 seemingly arbitrary numbers is selected. An there is no sign
of how to enter another number either. The key is, to click read, enter the number, and
then, before OK, select Term, which is not the default option.
For String, the situation is similar. At least, the fun, that takes the String literal, is
called stringLiteral, so it is easier to guess. But when one clicks read, things get more
complicated. As possible ‘types’ to read in the value, String and Term are offered. And
String is the wrong ‘type’ for String, since it will ask the parser of GF to parse the
entered value; but with the current restriction, that the GF parser cannot handle String
literals, this won’t work1 . One has to select Term and put quotation marks around the
String.
If one does not know this, this is a complete blocker. There is no indication, that one has
1
There is a special lexer for that, but that one is not active at this point.
42
7.7. Where is my boolean property?
to click outside the refinement menu to do a refinement. But why should it be necessary
to look somewhere else?
The reason, why integers cannot just be entered, when an Instance IntegerC is expected,
is that integer literals in GF have their own built-in type, Int. And this type has nothing
to do with Instance IntegerC, or Instance ? in general, for GF. They are completely
independent types. But semantically and linearisation-wise, they are quite the same.
Just that their type is different. To remedy that situation, a type conversion fun, the
aforementioned int, has to be used:
f u n int : Int -> Instance IntegerC ;
The linearisation of this fun just takes the string representation of that Int and uses it
uninflected:
l i n int i = np2inst ( numNoInfl i . s ) ;
The same holds for Instance StringC and String.
7.7. Where is my boolean property?
In GF OCL grammars, the OCL type Boolean is represented in more than one way.
Variables in the grammars have type Instance, so the Boolean variables have the type
Instance BooleanC. In natural language, an Instance is treated like a name, so itcan be
inserted whenever a subject or object of a sentence is expected. But boolean expressions
tend to be propositions like comparisons. These are sentences in NL, like the PIN code
is set, not just nouns. And cramping sentences into places, where a noun is expected
needs helping constructs like the truth value of the proposition that . . . or true iff . . . .
And they are a bit jolty and not used in normal NL.
But especially boolean operations can be applied to the sentences directly with ’or’ in
the PIN code is set or the card is uninitialised . They do not have to be treated as
nouns. Therefore, there is a cat Sent for those sentences for propositions and boolean
funs to conjoin them. Even a constraint itself, which in OCL has to have a boolean
value, is a Sent. But there are situations, where propositions and boolean variables have
to be mixed, for example when propositions are used as boolean parameters for methods
from the model. They expect Instance BooleanC. Therefore, ‘conversions’ from Sent to
Instance BooleanC and back have to be used.
Thus there are already two places, where boolean predicates could show up. But they are
neither Sent nor Instance Boolean. They are something else for the grammar generator.
The Predicate funs generated for them can only be accessed with a fun that returns
AtomSent. And these can only be accessed via the fun posAtom or negAtom, which
make Sentences out of them (an example tree can be seen in figure 7.5.
f u n posAtom : AtomSent -> Sent ;
f u n negAtom : AtomSent -> Sent ;
43
7. Usability Problems of the Previous Editor
Figure 7.5.: An AST, where a boolean model element is accessed.
f u n predCall : ( rec : Class ) -> Instance rec -> Predicate rec
-> AtomSent ;
This is a well hidden way that the user has to know. One would expect to be able to
access instances of Boolean to show up as Instance BooleanC, but that is not the case.
If predCall would be offered with a nice description when a Sentence is expected, it would
be easier to see. But hiding it behind the type AtomSent just makes it more complicated
than it should be.
Exceptions have the same problem. If one is thrown or not, is an atomic sentence
AtomSent, no Sent.
The reason, why there is the type AtomSent at all, is that negation of Sents leads to
awkward constructions like it is not the case that the PIN is validated , since a Sent has
no special negated form, which could be used instead of a lengthy sentence prefix. In
contrast, AtomSent has such a negated form. So the above example would be in English
the PIN is not validated , which is shorter and more easily read. So as to the produced
natural language, this approach yields better results.
But the GF resource grammars, which are used to a large extend in the grammars to
form sentences in a mostly language independent way, use the lincat Sent, and know
nothing about AtomSent2 . Although Sent is most often build with VerbGroups, that
have a parameter telling them to negate themselves if necessary, on the Sent level, this
negation is forgotten. And because of the compositionality of GF, that is, that one only
can access the linearisation record of a child in the AST, but never the subtree as such,
one cannot get back this negated form.
7.8. Everything is red, what did I do wrong?
If GF finds a constraint, that is, if arguments of dependent types to not match or have
not been filled in completely, it will turn the respective part of the linearisation red to
2
There is ongoing work in this area, so that is subject to change.
44
7.9. So many comparison operators?
make the user aware of this situation, as explained in the example in section 6.1.2. But
GF is a bit too cautious and reports such a case for the complete tree at the beginning.
This constraint only occurs because not all depending type arguments are filled in, so
the solve mechanism of GF can easily eliminate it by refining these arguments. But that
was not done automatically, so the user was confronted with the signal colour red as in
figure 7.1. This tells the user, that he did something wrong, which is not the best way
to greet a user. Especially, if that ‘error’ has no real basis.
7.9. So many comparison operators?
For a greater-than-comparison for example, there are two function for that, intGT and
realGT:
f u n intGT : (a , b : Instance IntegerC ) -> Sent ;
f u n realGT : (a , b : Instance RealC ) -> Sent ;
In contrast, in OCL, such comparisons are only defined on Reals. Also persons, who have
been shown the editor, asked why Integer and Real get different comparison operators.
One wants to compare numbers, and perhaps even a Real with an Integer, but does not
want to specifically compare only Integer.
The reason for these different comparison operators is, that an Instance IntegerC for GF
is not applicable when an Instance RealC is expected (see section 7.5 on coercions). In
order to spare the user of two coercions when Integers should be compared, the funs for
Instance IntegerC were introduced.
Also there are many funs to compare for equality: stringEq, intEq, realEq, boolEq, bagEq,
setEq, sequenceEq, sentEq, eq, neq, anyEq, anyNeq. A collectionEq was missing. What
the type-specific equality comparisons do might be easy to guess, but what do anyEq
and eq do?
anyEq is the comparison on OclAny, the supertype of all non-collection types in OCL. So
it should take all non-collections as possible arguments. What it does, with a necessary
coerce. eq is a parametrised equality where one first selects the type of both arguments
(perhaps one of them has to be upcasted, but that is not not explained to the user), and
then the arguments themselves.
But why is there a need for such a host of different equality comparison operators? The
reason is, that in OCL only instances of the the same type are allowed. And therefore,
every type has its own equality operator.
eq subsumes all other equality comparison operators, since with its type argument set
to String for example, it expects exactly the same arguments as stringEq does, only that
the type selection is deferred to a later step. One could say, that this serves as grouping
of several equalities. eq can even play the role of anyEq since OclAnyC for the grammars
is just a Class, as StringC is one.
45
7. Usability Problems of the Previous Editor
With so many ‘different’ comparison operators, the user is given an unnecessary choice,
which needlessly puts more mental work on him.
7.10. I wanted to fill in the next question mark . . .
When one metavariable, or as it is presented, a question mark, is completely refined,
GF jumps seemingly randomly to the next, or sometimes previous metavariable. It does
not always stay in the current subtree, where the mental focus of the user is, but often
leaps up to previous unrefined nodes. Which distracts the user from the goal he wanted
to refine, since he is drawn away by force.
Theoretically, GF should stay in the same subtree, so this behaviour is to be considered
a bug (even by the main GF developer Aarne Ranta).
7.11. I could choose self, but now nothing is offered.
In the OCL GF grammars there is one fun for self and one for result.
f u n self : ( c : Class ) -> VarSelf c -> Instance c ;
f u n result : ( c : Class ) -> VarResult c -> Instance c ;
result, in English for example, is always linearised as the result, while self is linearised
as the pay card , if the class of self is PayCard, or in general, the c for a class c. For this
linearisation, the class argument of self is used.
But the VarSelf/VarResult3 arguments do not appear in the linearisation. And there are
no funs returning these types. These only occur as special bound variables introduced
in an OCL context, where self and result actually can be used, and then they are given
the correct type, as visible in the following example:
fun NOPACKAGEP_PayCardJuniorC_available_Oper_Constr : (
VarSelf N O P A C K A G E P _ P a y C a r d J un i o r C -> VarResult IntegerC
-> Ope rC on st ra in tB od y ) -> Constraint ;
When this fun is applied, there will be one bound variable of type VarSelf NOPACKAGEP PayCardJuniorC, but apart from that, no other funs or variables of type VarSelf
SomeType. So self with its arguments can only be fully refined if the Class parameter of
self is IntegerC. Otherwise, no VarSelf variable/fun would be applicable for the second
argument.
And that is a problem for the editor. GF tolerates arguments for which there are no
possible refinements. So self can be used for any class, since it returns an Instance c, and
the c can be set arbitrarily for GF, with the missing refinement for the VarSelf argument
3
Later on, only self will be mentioned, but result is also meant, both are treated the same way except
when noted otherwise.
46
7.12. Why does the tree always collapse my nodes?
being no problem. So self will always be offered in the refinement menu.
Only in cases, where the supertype is OclAny4 or unrefined, this is no problem, since
every model class or return type would be allowed there, including the correct one.
And refining with self at a position where it is not applicable leads to a situation where
a VarSelf c fun is expected, but no fun for VarSelf c exists. And the user will only see,
that there is nothing offered in the refinement menu, as shown in figure 7.6. In fact,
that is the same behaviour, as when a type-incorrect upcast with coerce is tried: One
guard argument cannot be filled in. But that that really is an error, is not transmitted
to the user.
Figure 7.6.: The user refined with self for a wrong type and is now stuck.
7.12. Why does the tree always collapse my nodes?
As mentioned in section 8.1, the editor is stateless over several GF runs. This also means,
that the graphical representation of the abstract syntax tree is rebuilt from scratch after
every command sent to GF that changes the state of GF. So there is no memory of
which nodes where expanded before.
4
Note, that actually there is one exception to this: If an operation returns an array, this will be
mapped to the OCL type Sequence by the grammar generator. And then result will not have a type,
which is a subtype of OclAny. But this is ignored in the editor right now.
47
7. Usability Problems of the Previous Editor
48
8. System Architecture
8.1. Overview
TogetherCC
OCL old OCL UML model
KeY
OCL Parser
UML Model
Grammar Generator
AST
OCL GF Grammars
Model Grammar
new OCL
GF
Commands
State
GF Editor
Figure 8.1.: An overview over the different components, and how they interact which
each other
The Java Editor is principally just a GUI for authoring documents with GF. What it
does is displaying the editing state of GF, which runs as a separate process, and giving
the user the possibility to interact with this state. The editor does nothing with the
commands the user gives themselves, but instead sends them to GF to process them.
After that GF sends the new complete state to the editor, which displays it to the user.
The GUI itself is nearly completely stateless over consecutively issued commands. That
means, that the editor forgets everything about the tree and the linearisations before
receiving them anew. Being stateless is one of the main design decisions of the editor.
Although the editor ‘talks’ only to GF, there are more components involved, in order
to actually get the OCL constraints into GF and thereby into the editor. These are
shown in figure 8.1. The system plugs in into TogetherCC, a CASE tool, as mentioned
in section 6.2. TogetherCC features a so called single-source model. That means, that
the UML model is not saved in an extra representation, but is always recreated from
49
8. System Architecture
the class files themselves. Because of that the class files also were the logical place to
store the OCL constraints, which are annotations to the model. This is implemented
with custom JavaDoc comments and used throughout KeY.
A plug-in of TogetherCC takes the model and exports it into an easily parseable format
which is read by the grammar generator1 . Then the grammar generator creates GF
grammar modules for each class and property in the model. Not only the abstract
grammar is created that way, but also concrete grammars for OCL, English and German.
If a previous OCL constraint is available, the plug-in then feeds model and constraint
into a special OCL parser and type-checker2 , which in return gives the GF AST for
the OCL constraint back. This AST, or if no previous OCL constraint was found, a
generated skeleton, is then given to GF. Then the editor is started and the constraint
can be edited there.
The GF grammars for the standard OCL functions and types are used in GF and thereby
can be used by the user in the editor.
After finishing editing/creating an OCL constraint it is given to the plug-in, which tells
TogetherCC to save it as a JavaDoc comment. If the constraint is not yet finished, it is
saved directly as the GF AST since the parser is not able to parse incorrect/incomplete
OCL. A finished OCL constraint, which is saved as standard OCL, can now be used
with the KeY prover.
8.2. State of the affairs at the beginning of this work
In 2003, Kristofer Johanisson integrated the editor and his grammars into TogetherCC.
With that, it was possible to call the editor from the context menu of a class or method in
the CASE tool to edit a class invariant or pre- postcondition pair. A Java class exported
the elements of the UML model into funs for GF, so they could be used as elements of
the AST. When closing the editor after ‘writing’ a constraint, it was saved as both OCL
and in the GF AST representation as a custom JavaDoc comment for the respective
class or method. Saving in GF AST syntax was necessary since due to circular rules in
the grammars (see [HJR02] for some more details) OCL could not be parsed with the
built-in GF parser.
The integration had been done for GF 1.1, which didn’t feature the module system introduced in GF 2.0. But in [Dan03], together with the creation of the German grammars,
the grammars for OCL, and later for English, were updated to use this module system.
That meant, that the improvements implemented later, like support for Java packages
and the formatting mechanism of David Burke in [BJ05], were not usable in the version
that had been integrated in TogetherCC.
In [Joh04] a system for disambiguating implicit OCL constructions is presented. The
1
2
written by Kristofer Johanisson in Haskell
also written in Haskell by Kristofer Johanisson
50
8.3. Support for the new grammars
reason for this was to build an OCL parser which can be used for the GF OCL grammars.
This parser was working at the beginning of this thesis, but it was not integrated into
TogetherCC, so parsing OCL was still not supported there.
[Johar] describes the state of the system after the author implemented the integration
of the new grammars using the OCL parser. Since [Johar] was written around the
time when this work was started, it mentions none of the improvements of the editor
introduced in this work, but the basic work-flow has not been changed, so with regard
to that it is still valid.
8.3. Support for the new grammars
In section 8.2 above several unconnected components are mentioned. Thence, the task
was more or less to wire them together.
The model export into the immediate representation was already implemented, but only
used to create such an export file for the batch translation of OCL constraints in a
separate file. A reference to this export file is given to the grammar generator. The
grammar generator takes this file and generates the GF grammar files in a temporary
directory.
For the OCL parser to work, an OCL file with package and context information is
needed. This file is expected to have a layout like the one seen in figure 8.2. For the
parser, package and context declaration are necessary, but since the OCL constraints in
TogetherCC are already attached to their respective context, they do not contain them,
do not need them and also shouldn’t contain that redundant information. Hence, they
are added to the OCL by the plug-in.
After parsing, the package part is stripped from the GF AST, since it is assumed, that
the user does not want to see which package he is in all the time. The context part
on the other hand is needed by GF, as bound variables like operation parameters are
declared there. Without this part in the AST, they could not be used in the editor.
When no previous OCL constraint is available, a skeleton is created (see figure 8.3 for
an example). This skeleton only includes the context, but no package declaration, since
it is created on the Java side and not within the OCL parser where the package would
be needed. That brings a synchronisation problem regarding naming conventions with
it. The plug-in has to produce the same fun names as the grammar generator does,
since the skeleton may only include existing funs. If the grammar creator changes, the
plug-in has to be adapted as well.
When the skeleton is created, the names of the bound variables can be set to the actual
parameter names from the model. The skeleton is then given to GF as a tree. Alternatively, the editor could refine the tree step by step, as a user starting from scratch would
do. But that way, the parameter names would be x 1, x 23 and so on, which simply
would not match the model.
3
They could get renamed with the alpha command, but why make it unnecessarily complicated?
51
8. System Architecture
package javacard :: framework
c o n t e x t AID
inv :
not ( self . theAID = null )
inv :
self . theAID - > size () >= 5
inv :
self . theAID - > size () <= 16
c o n t e x t KeyEncryption :: setKeyCipher ( keyCipher : Cipher )
p o s t : cipher = keyCipher
endpackage
Figure 8.2.: An example OCL file as expected by the OCL parser. This one is taken
from [BJ05].
NOPACKAGEP_PayCardC_charge_IntegerC_Oper_Constr (
\ this , amount -> prepostCt
?
?
)
Figure 8.3.: An example OCL skeleton for the method charge of the PayCard example as
a GF AST. The first line sets up the context. In the second line two bound
variables are introduced in the subtree starting with prepostCt. This fun
takes the pre- and the postcondition as its arguments, which are the question
marks in the next two lines. They are question marks in the skeleton,
because they are not yet refined, but instead open nodes, or metavariables
as they are called in GF.
A restriction of TogetherCC for the current saving mechanism is, that every custom
JavaDoc tag can only be saved once per class/method. That limits the number of invariants, pre and postconditions to one, although in OCL, more are allowed, as visible in
figure 8.2. To prevent the user from creating unsaveable OCL constraints, the skeleton
for method contracts contains a special fun prepostCt, that does not take lists of pre/postconditions as its arguments, but just one Sent for each of them (see figure 8.3). For
class invariants, the fun invCt is used, which takes only one Sent as an argument. The
parser, in contrast, returns list constructions, even if only one invariant, precondition or
postcondition is in the parsed OCL. That is a known, but not yet fixed, deficiency.
If no parseable OCL, but a GF AST is found in the JavaDoc, that one is used instead.
But parseable OCL will be preferred over the GF annotation, and only one of both
52
8.4. Basic work-flow
is saved, while the other one, if present, is deleted. That way a need for merging is
circumvented. The GF AST is expected never to be changed by hand, and if complete
OCL shows up in the JavaDoc, then this has to be more recent, since otherwise the
editor would have deleted it. This OCL is taken instead and the GF AST is deleted
when the constraint is saved the next time.
The editor is given a callback class, that takes care of saving the OCL as JavaDoc comments when closing the program. That way the editor has no compile-time dependency
on TogetherCC. The callback classes are instantiated in the interface plug-in and refine
an abstract class which just offers the saving methods, but does not depend on TogetherCC itself. And only this abstract class is referenced in the main editor class. The
callback classes strip the context information from the OCL linearisation given by GF
and in case of a method contract, splits the constraint into the pre- and the postcondition, to save them independently.
When all things are set up, GF is told where the temporary grammar files reside and
started. When the grammars are loaded the AST is given to GF. Now the user can start
editing.
8.4. Basic work-flow
The editor is started from the context menu of class or method (as in figure 6.10). The
user is then at a node of type Sent, which in this context means a boolean proposition
(as explained in section 7.7). The description above the refinement menu tells us4 , that
we are editing the precondition of a method. Here we can select one boolean proposition.
If we want more than one precondition, we have to use andS to conjoin several of them
conjunctivly, due to reasons explained in section 8.3. There, one can select the wanted
proposition from the list in the refinement menu and double-click it. This command is
then executed and the state updated. Then the work continues with the standard editing
steps as described in section 6.1.3, just that the grammar will allow one to construct
OCL constraints instead of stone age sentences.
Intermediate saving in the model is not yet supported, the save buttons will save the
tree or linearization in an extra file. Only when exiting, the user is asked, if he wants to
save the constraint.
4
This section anticipates the state of the editor after the implementation of the improvements mentioned in section 9.
53
8. System Architecture
Figure 8.4.: The editor after choosing Edit Pre/Postcondition [GF] in the context
menu of TogetherCC. An entry in the refinement menu has already been
clicked, but not been sent to GF (needs a double-click).
54
9. Usability Improvements
In this chapter it is described, what has been done to overcome the problems found in
section 7. But some of the items below have not been listed in this list, nevertheless
they have been regarded as worthy of improvement.
Everything here except the ideas in section 9.6, the parts of section 9.3 marked as such
and the discarded ideas in section 9.1 has been implemented.
9.1. The next refinement ...
9.1.1. Refinement descriptions
The problem in section 7.1 is, that the refinement menu is a huge list of cryptic names.
And even if the names were understandable, they would not help someone who does
not know what the respective OCL functions do. As a solution, the names could be
blown up in their size with a special nomenclature, that is rendered in a special way.
Underscores could be replaced by spaces and so on. But, although it would be possible,
that would make the fun names very unwieldy and cumbersome for the grammar writer
and the AST unreadable, since the funs are shown there in their full glory (sth. which
would have to be changed then). Also changing a description would make unfinished
constraints unloadable, since the old descriptions are still used as the fun names in the
saved GF tree. Descriptions do not play any purely functional role as the fun names
do. Therefore, description and abstract syntax name should stay separate and solutions
besides the abstract fun names need to be found.
In the editor one can switch the language of the refinement menu. That means that
the linearisation of each fun in the selected language is displayed there instead of the
abstract fun name. In figure 9.1 one can see an example for that. Since GF does not
know how the arguments of each function are linearised, their type with an appended
number is used instead. Most often this will just be Instance. What kind of Instance is
not displayed. So for example, collection functions cannot be recognised that way.
Also entries like r [] can occur this way. This happens, because the linearisation of
lists depend on the number of list entries and is defined in a table. And GF defaults
to the first parameter, which is normally 0. And the emtpy list gets linearised as the
cryptic []. To remedy this, the tables for parameters have to get their order changed.
But the other problems described in this sections wouldn’t get away with that.
55
9. Usability Improvements
Figure 9.1.: The situation from 7.1, but this time with English as the menu language
There is yet another way to get information into the refinement menu. GF offers a
mechanism called printnames. In the grammars they are defined like in the following:
-- in the abstract grammar :
fun
cryptic : OneType -> AnotherType ;
-- in a concrete grammar :
lin _ _
cryptic = " not very helpful linearisation " ;
p r i n t n a m e cryptic = " speaking name " ;
Technically, printnames are just strings. They can be added to any fun in a concrete
grammar module. This string will then be displayed in the refinement menu instead of
the abstract fun name. Helping the user to recognise what a fun does, is possible with
those printnames, but in the case of OCL this hasn’t been done before this work began.
But this one line string still does not offer enough place for a description of how the
OCL operation is defined or what it exactly does.
Tooltips could help here. Although they shouldn’t get too long, more than one short
line as what a name would consist of would be possible. The tooltips could contain a
short description of the command, what it does and what it can be used for. They could
be further enriched with information about their parameters, as, for example, JavaDoc
offers it. An example for that is shown in figure 9.2. For properties of model elements,
their fully qualified name can be given like in the following:
56
9.1. The next refinement ...
But there existed no way to display tooltips which are different from the displayed text,
and, not to mention, a way to tie that different text to the funs. How this is actually
done is discussed in section 9.1.4.
Figure 9.2.: The OCL collection operation forAll, explained with a tooltip
9.1.2. Grouping of refinement menu entries
At the beginning of this work, the refinement list featured no grouping of the offered
funs or hierarchical access to them. In a way they are already grouped in GF, since at
any place in the AST, only type-correct refinements are offered and the others not. But
since GF features no subtyping, no further subdivisions are possible on the type level.
So we need a different way to tag funs.
One way would be a special nomenclature. The fun names could get a special prefix,
perhaps with as the divisor character between category/namespace name and the real
fun name. All funs with the same namespace prefix would get into the same submenu
of the refinement menu.
But that way, those prefixes could not contain special characters and at least one of the
funs would have to have a huge name consisting of the complete description, which would
be unwieldy. Thus, to give these prefixes longer descriptions, one could use one special
cat Namespace in GF. Now introduce a special fun of this cat for every prefix and give
it a speaking name as the printname. This printname can then be used as submenu for
all funs of one namespace, with the mapping between prefix and description handled by
57
9. Usability Improvements
the editor. Even a namespace hierarchy is possible that way.
But as explained above, changing the submenu of a fun would make old trees unloadable,
but at least the descriptions of the subcategories can be changed without that problem.
Another way would use the module system of GF by putting all funs of each subcategory
into a separate module. This would greatly increase the number of files, but since the
definitions of funs do not depend on each other, this would be feasable. But then one
would have to keep track of this number of files, where paragraphs would just do the
same. Which boils down to a question of style, if that is to be an advantage or not.
Giving the modules better display names would again have to be done with special funs
and their printnames, since modules do not appear in the abstract syntax as palpable
entities; they just organise. And finally, renaming a subcategory would be a bit brittle,
since one would have to change the name in all files using the changed module.
With both ways, the grouping would be available on the abstract syntax level, since it is
encoded in the fun names. With special fun names that would be directly the case, while
with modules the fully qualified name would become sth. like module subcat.funName
Since fun are given with this fully qualified name in the list of possible commands for
the refinement menu, this grouping information would always be available. Thus, for
both ways, the grouping would be available for all menu languages in the editor.
9.1.3. Parameter descriptions
In helping the user to write OCL, there are two different levels. The more abstract
reliefs the user from formulating OCL at all. To help there would be one step up on
the semantic level, perhaps something like a wizard system or the specification patterns
described in [BH03]. The user somehow chooses wanted properties and OCL is generated
for them.
On a more concrete level only the syntax is done by the system, the OCL is still formulated, but not written, by the user. That is the level this work operates on, to help
people writing OCL, who know what they want to formulate, but do not know its syntax. This part is taken over by the grammars and GF, so that the user does not need to
know the OCL syntax. But the user still sometimes has to know about the grammars.
At any time one refines a child node of another node (the top node is inserted automatically on start-up). But it is not clear for all of these child nodes, what is actually
expected, as it is in figure 9.3 for example. Especially hidden type arguments are hard
to guess. Figure 9.4 is an example for that.
Here two ways are possible. One says, that these type arguments only deal with the
lack of subtyping in GF and should be hidden from the user completly, since they only
specify the types of other Instances, and these other Instance arguments really matter.
Only they show up in the linearisation. If they are refined, the types can be filled in
automatically.
In OCL, upcasts are done implicitly, and why bother the user at all with having to deal
58
9.1. The next refinement ...
Figure 9.3.: Without additional help, the linearisation gives enough clue, what this sentence in the implication represents.
Figure 9.4.: Here one can only guess, what this hidden type argument does.
59
9. Usability Improvements
with it explicitly? Why spend energy on making things easier, that shouldn’t be there
at all? And in the author’s opinion, that is a valid point.
But as long as types of arguments are not yet refined, GF cannot help with reducing the
offered refinements (e. g. in figure 9.5). Selecting first the type, and then the instance
will result in a smaller refinement menu (as in figure 9.6). funs, whose return type again
depends on one of their arguments, will still be offered, though. But if the argument on
which the expected type depends is chosen, GF can automatically set the type argument
of the callee, as shown in an example in figure 9.7. The earlier such an argument is
refined, the more GF can do things automatically, because it knows, what to do. And
that makes helping the user to refine early something worthwhile again.
Figure 9.5.: The receiver argument of propCall not refined, all properties of all classes
listed
60
9.1. The next refinement ...
Figure 9.6.: Now the receiver class is fixed and thus, only the properties of that class
listed
Figure 9.7.: The type argument of eq has been refined to Integer. With that, the type
of the arguments of eq is fixed to Instance IntegerC. The return type of
propCall in turn depends on its second argument, so that one could be filled
in automatically.
61
9. Usability Improvements
Thus a way is needed to add descriptions to parameters. Then, when the user wants
to refine a parameter, the editor can tell him, what this argument does and what is
expected. This would be a means to help with the problem described in section 7.3.
Tooltips, again, can help here. Thus, all unrefined nodes in the graphical AST representation get the parameter description of their parent node that belongs to them as
a tooltip, as shown in figure 9.8. But when refining, the mouse cursor is not always
in the tree. So the generic sentence Select Action on Subterm above the refinement
menu is replaced by the current parameter description. That way, this label gets more
specific and helpful, even to a user who has used the editor before and knows, what the
refinement menu does. This also is shown in an example in figure 9.8.
Figure 9.8.: Figure 9.4 revisited: The text above the refinement menu and the tooltip in
the AST explain, what the current refinement is for.
But this information needs to be known in some way. Somehow, it must be attached to
the parameters of the funs. Normally, in the abstract syntax, they do not have to be
mentioned by name, since only the types are relevant there.
f u n negAtom : AtomSent -> Sent ;
f u n eq : ( c : Class ) -> (a , b : Instance c ) -> Sent ;
For dependent types like in eq, the names have to be given and will be remembered,
but for unnamed arguments, they are given an internal name, as visible in the following
excerpt form the compiled form of the above grammar snippet:
62
9.1. The next refinement ...
f u n negAtom : ( h_ : core . AtomSent ) -> core . Sent = {} ;
f u n eq : ( c : core . Class ) -> ( a : core . Instance $ c ) -> ( b : core .
Instance $ c ) -> core . Sent = {} ;
It is possible to give all arguments names, and with the current state of GF they would
even get remembered too, but nothing in the theory behind GF guarantees that. Alpha
renaming is allowed there, but just not done (yet). So it is not a save bet for the future,
to store the names there.
And in the concrete compiled form, the arguments are just numbered, so names given
there are also forgotten after compilation.
So GF’s names for the parameters cannot be used to display them to the user. And,
to go further, names do not make descriptions, so that problem cannot be solved with
GF’s own means alone, but would again need some nomenclature. Which this time
would survive saving and loading of ASTs since the parameter names do not show up
there, but are also not guaranteed to be remembered by GF. Also, as mentioned above,
the fun names only theoretically could get overloaded, but that is to be dismissed due
the practical problems they bring along.
9.1.4. Printnames as the solution
The problem in sections 9.1.1, 9.1.2 and 9.1.3 is, that, as explained there, information is
needed which is tied to funs. As explained above, this cannot be done with abusing the
fun names. But a solution, that allows for extended fun descriptions, could also include
a tag for grouping, so that wouldn’t need an extra way.
Since the information needs to be tied to the funs of GF. Having them in extra files just
increases the chance, that one forgets to update the descriptions to match the new state
of the grammars, so that was ruled out.
A solution would be to put them as comments in the abstract grammar module, like
JavaDoc1 does it for Java source files. But that would require an extra parser for .gf-files
and also access to those files at runtime.
That led to the idea of looking at printnames again. For GF, they are just strings with
no internal structure. They are parsed by GF and can be accessed there at runtime. In
fact, GF sends them to the editor for each command it offers in the refinement menu.
That way the mapping of fun and its printname is done by GF. Why not put structure
in those strings and parse them when needed, as thought on for the overloaded fun
names?
The tooltips mentioned in section 9.1.1 are a direct candidate for that, and therefore
have been implemented that way. One part is displayed as the display name of a fun
as before, but the rest is reserved for the tooltip. Since printnames have no stringent
length restrictions, they can get as long as it suits the grammarian and as it is suitable
1
http://java.sun.com/j2se/javadoc/
63
9. Usability Improvements
for a tooltip.
Another tag was added to the printnames to assign them their subcategory in the refinement menu. This tag can take an optional argument that becomes the display text in
the refinement menu, and may also feature a tooltip. But one problem arises here. GF
sends only those printnames to the editor, whose funs are applicable. But the description
would be needed everytime when at least two members of a subcategory are available
(see below). And lengthier descriptions should be written only once, and applying them
for all funs with the same tag shouldn’t depend on copy&paste.
In addition, parsing printnames also takes some time. So if parsing them all over again
and again, when GF sends something to the editor, could be avoided, that would be a
good thing. Since printnames are just strings, they do not depend on anything else and
thus never change.
Thus they are read once at the beginning and the read printnames get cached in their
parsed representation. And when all printnames are together, the tag with the description argument is available, too , so all other funs of the same subcategory can share this
description. This description is then available for all printnames of that subcategory.
hen there is only one fun of one subcategory applicable, there is no need to hide it in a
submenu. That would just make it less visible and access to it harder, but without the
advantage of reducing the initial menu. So whenever there is a subcategory with just
one available fun, this fun gets displayed on the main level of the refinement menu.
In the current system, no further hierarchy for refinement menu entries is supported
since it is not needed. There never are too many entries in a submenu to justify that
work, but still the tag could feature a ’.’ for example as a divisor between namespace
parts. Thus a hierarchy would be possible, if the need for that arises at a later point.
As to the parameter descriptions of section 9.1.3, these were accomplished with an equivalent of JavaDoc’s @param. One just lists parameter names together with a description
in the order, which the respective parameters have in the type signature of the fun. This
order, or to be more precise, the mapping between description and actual parameter,
can’t be checked by the compiler, since without reliable names (it is not guaranteed,
that they are the same as in the fun definition at runtime) there is no other way anyway
to tell, for example, two parameters of the same type apart.
The syntax of these extended printnames is explained in detail in appendix A.
Another way would be to change the printnames in GF from simple strings to records,
which contain the aforementioned tags and values as fields. Grouping tag, display and
tooltip text would be present in all printnames while the number of parameters would
vary. If the enhanced printnames get used in other grammars beside the OCL ones, this
still can be done later, but only one grammar did not justify such a change in GF.
One aspect that has to mentioned, is that printnames belong to a concrete grammar.
They are not supported on the abstract syntax level. So they can only be used if this
grammar is loaded. For editing OCL constraints that means, that when only English or
German is loaded, none of the aforementioned improvements are available.
64
9.2. HTML support
But having this information in the concrete grammars can be advantageous, too. This
way, different sets of tooltips would be possible. A more technical description for persons
who know about formal specification, but don’t know OCL, like written for this work
would be one way. Another would be a much more descriptive version which tries to
minimise the use of technical terms, giving examples, a bit like an OCL tutorial. Or
just a translated version. Currently, German is supported as a linearisation language,
but the tooltips will always be in English. With different printname sets, with selecting
the menu language, one could choose the wanted set of tooltips.
The tooltips of GF evolved in this work. At the beginning, they were just clearer
names, whereas they now perform a bigger documentary purpose. They now explain and
structure the offered refinements and can even guide the user, increasing their usefulness.
9.2. HTML support
In section 7.4 a lack of formatting was stated. It was also explained, why supporting a
mark-up language is not possible with the old approach. What is needed, is a mapping
of a position in the output area to the AST position of the linearisation snippet, that
is displayed there. With text, this is quite easy, since text always has the same length
in characters in Java. As described in section 10.1.6, with HTML, the displayed length
depends on the used HTML tags and not just in their length in characters.
HTML is a hypertext language, and thus offers links. So why not use links to do this?
The answer is, that the snippets from GF can look like <b>. The closing tag may come
later on. Enclosing such a semi-tag with a link would result in <a href="[AST,position
]"><b></a>, which would be illegal nesting. And the Swing HTML renderer just ignores
these lone formatting tags. So putting everything inside links does not work out.
One easy and fast way would be to strip the HTML tags from the output, when doing
the character counting. For basic markup like <b>foo</b> that would work, but already
<br> for a line break would mess this up, since the carriage return does count as a
character for Swing, but with stripped tags, it would not be counted when creating the
mapping.
So the editor has to look inside the HTML tags; the HTML has to be parsed to determine,
how many characters the output may have. But writing a special HTML parser was
considered too much of an effort. This lead to the idea to try out, how good the Swing
HTML renderer is at calculating the displayed length of incomplete HTML. A not shown
HTML area gets the HTML text in all its stages, one snippet appended after the other.
For every snippet, the length of the (not) displayed HTML before and after appending
is remembered. After the linearisation is complete, it can be rendered on the screen, but
the character positions of the individual snippets are still the same.
This approach works for all HTML tags, which are forward-readable, that is, for those,
which do not, when they get appended, change the positions of the already displayed
65
9. Usability Improvements
characters retroactively. Luckily, for all tags used in the HTML formatting module by
David Burke, this is the case. The benefits of putting structure into OCL constraints
can be seen in figure 9.10. And even pictures can be displayed and clicked that way, as
shown in figure 9.9 with a different grammar, not one of the OCL grammars.
Figure 9.9.: A picture in the HTML display. Although pictures are not highlighted
when clicked, the corresponding node in the AST is still selected. Left of
the rendered HTML is the HTML source code. Both can be shown side by
side, if wanted.
The same way of character counting is also applicable for pure text, except that a simple
StringBuffer is enough, and no Swing component has to be used for appending. And
as an added bonus, the code for marking the length of the StringBuffer before and
after appending is much shorter and easier to understand than all the special cases that
were necessary when the positions in the XML string, which was changed, when tags
were removed, were calculated.
9.3. Coercions
As mentioned in section 7.5, type coercions have to be introduced manually since GF
lacks in-built subtyping. So upcasts/coercions have to be made explicit (see figure 9.11(a)
for an example). But putting that burden on the user complicates his task. Coercions in
UML are implicit, and that is, what the user expects if he knows UML, something which
can be expected, since otherwise a CASE tool which builds upon UML like TogetherCC
66
9.3. Coercions
(a) No formatting used
(b) HTML is used to format the OCL
Figure 9.10.: Side by side comparison of no formatting and formatting
would not have been used in the first place. Having explicit type coercion clashes with
the user’s model, which is something like in figure 9.11(b). So, if possible, they should
stay hidden and implicitly inserted.
9.3.1. Where to insert?
The idea is to introduce coercions automatically at places where the access of subclasses
is expected and to hide them as long as possible. If everything goes well, coercions
should not be shown at all. But when to introduce a coercion? There are reflexive
subtyping witnesses, so adding a coercion even if an instance of the supertype is chosen
as the refinement later on does not hurt (as long things go well, see below). On this
condition, they could be inserted whenever an instance of a superclass is expected.
But that is a bit too often, since a number of funs have their own type arguments, like
propCall:
propCall : ( rec , ret : Class ) -> Instance rec ->
-> Instance ret ;
Property rec ret
67
9. Usability Improvements
(a) Necessary coerces shown. That’s how GF
wants it.
(b) No visible signs of
coerce. That’s what the
user expects.
Figure 9.11.: Different expectations for coerce
There is no need to coerce the Instance argument, since its type can be set with the first
argument rec directly to the type of the receiver. No supertype is involved here.
Having one type depending on several arguments creates a constraint in GF. When all
those type arguments match, everything is fine. But if one is changed, the constraint is
unsolvable for GF and reported as a clash. Minimising the number of possible clashes
by minimising the number of arguments which depend on others without restricting the
user is a good thing.
So how to decide, when a coerce should automatically be inserted, and when not? Apparently that depends on the funs themselves. A rule of thumb is, that if the expected
Instance already depends on a type argument like in propCall, and if it is the only such
Instance, than no coercion should be introduced. When there are two such Instances, like
in eq (already printed in section 9.1.3), then this type probably is a common supertype.
In these cases at least one coerce is necessary. So better introduce two here, but hide
them. Refining the first argument will most probably make GF fill in the type argument.
If the type of an Instance is already defined in the grammar, like it is often the case for
Collection, then most likely a coercion is needed, since those operations are often meant
to be general. That is the case for most user defined operations from the model. Here,
an automatic coercion allows refining with subtypes. But again there are exceptions.
It is unlikely that there are subtypes of Integer, Boolean and String. In Java, which is
the main supported language of KeY and the editor, one even cannot inherit from those
types. So here an automatic refinement with coerce can hardly do good, but nearly only
harm. So again, a way to tag funs, or to be more precise, to tag their parameters, is
needed.
68
9.3. Coercions
9.3.2. How to tag?
CoercedTo If GF for an argument finds only one suitable refinement, it refines that
automatically. That led to the idea of a special cat CoercedTo:
cat
cat
cat
fun
Instance Class ;
CoercedTo Class ;
Subtype ( sub , sup : Class ) ;
coerce : (b , a : Class ) -> Subtype b a -> Instance b ->
CoercedTo a ;
This category stands for values, that have been upcasted, whereas Instance represents
an uncoerced type. coerce is the only fun that returns this type, so it will be applied
automatically, whenever the fun expects CoercedTo in its type signature. Thus, tagging
as mentioned above will take place in the fun signature as in the following example:
f u n taggedFun : ( a : CoercedTo A ) -> ( b : Instance B ) ->
Instance ReturnType ;
The first argument will, as said above, always be refined with a coerce by GF an thus,
all subtypes of A can be used here. The second argument is fixed to Instance B. No
coercion is possible here. This way, coercions are applied by GF, and the editor does
not have to do that.
Printnames Another approach is using printnames again. A special flag in the parameter description signals that a coerce has to be introduced here. The flag could be given
for any type, but will have an effect only for Instance. If the selected node is an unrefined
node, for which the parameter description includes that flag, then the editor will, before
the current situation is displayed, tell GF to refine with coerce and move to its Instance
argument (the latter is even with CoercedTo something the editor has to do). Since the
flag is in the printnames, which for GF are just strings, GF will do nothing by itself.
Nothing in the types enforces an application of coerce. It’s all on the editor side.
As it will be explained in section 9.3.5 the approach with CoercedTo has been discarded.
So tagging is done with printnames.
9.3.3. Refining
The wanted way to introduce coerce automatically is shown in figure 9.12. As explained
above, the editor automatically selects the Instance argument of coerce, and if the coerce
is hidden in the tree, only this Instance argument can be selected, since the coerce itself
is invisible in the linearisation because also in OCL, subtyping is implicit and invisible.
In the following, the definition of coerce in the abstract grammar (repeated from 7.5) is
presented:
69
9. Usability Improvements
(a) The second argument is about to be selected by the user
(b) coerce has automatically been applied and
the cursor switched to
the Instance argument
Figure 9.12.: Before and after an automatic coerce application. For the user, coerce will
be hidden, along with its first three arguments, but to look behind the
scenes, its shown here.
f u n coerce : ( sub , super : Class ) -> Subtype sub super -> Instance
sub -> Instance super ;
The normal case is, that super is already fixed. For example, when refining an argument
for a model query, each parameter of that query has a determined type. So not all types
are possible subtypes (as it would be, if super wasn’t known). But GF still offers all
types of Instances for the sub argument, since it does not care if the Subtype argument is
refinable or not. That is the same behaviour as in section 7.11 with the VarSelf variable.
Although GF does not care about that, the editor has to care, since it is its goal to help
the user by preventing type errors.
So in cases where the editor can expect, that only a limited range of subclasses are
possible, that is when super is neither unrefined or OclAnyC, it can try to offer a reduced
refinement menu for the Instance argument. To do that in the background, the editor
moves the focus to the Subtype argument and gets the list of possible refinements there.
Only subtyping witnesses are listed, where the superclass is super. Now for each one
of those, it refines with it and moves to the Instance argument. The funs offered there
will be suiting for this subclass of super and get stored. Then move and refinement are
undone, and the next subtype is refined, and so on. After all subclasses are tried out, the
stored lists of funs are packed into one refinement list with duplicates removed, which
is then shown to the user.
Duplicates occur because quite a number of funs have a return type that depends on
one of their arguments. And that, theoretically, could be any type. So they could be
applied wherever an Instance is expected, and thus will occur in each of the collected
lists. So the duplicate removal is necessary.
The refinement menu does now contain no more funs, whose type is clearly not a subtype
of super.
70
9.3. Coercions
(a) The Instance argument of a coerce
is to be deleted
(b) The coerce has also been deleted
and is refined again
Figure 9.13.: Before and after a delete of a coerced Instance
9.3.4. Deleting
When a refined node is selected, the refinement menu will contain the command to delete
that node with all its children. But that is not enough of nodes below a coerce. For
a new refinement the user might want to refine with a different subtype, and not with
the old one again. But the type arguments of the coerce above are still the same, since
they are hidden and cannot be deleted directly by the user. Thus, GF will only offer the
same subtype again. So these type arguments have to be removed. The original state is
shown in figure 9.13(a) and the target state in figure 9.13(b).
This is implemented with a chain command. The delete command is modified, so that
first the cursor is moved to the coerce, then the delete is issued and finally, the coerce is
reintroduced. If the expected supertype is already known, GF will refine the return type
argument automatically, what it wouldn’t, if just the return type argument would have
been deleted explicitly because GF would respect that and not refine it immediately
again.
Deletion of the coerce might also be necessary, if the Instance node has not yet been
defined. For eq, the parametrised equality, one might first choose the type on which the
comparison should be done. When one of the arguments is selected then, the supertype
of the automatically introduced coerce will be set to that type. But when the user
now goes back and changes or deletes that type argument, a clash will occur, since the
supertype of the coerce, on which the return type of the coerce depends, is no more the
same. The coerce now becomes visible. But if the user just selects the Instance again, the
coerce will automatically be deleted and reinserted. Figure 9.14 shows the state before
and after this.
So that clash is solved nearly without user-interaction. Making this deletion completely
automatically would be preferable, though. But that would make necessary that all
nodes, that could theoretically depend on the currently changed node, get checked if
they have to be deleted or not. And that is not done at the moment.
71
9. Usability Improvements
(a) The argument, on which the type
of the Instance arguments depends, has
been changed, so GF reports a constraint.
(b) After the previously selected Instance argument has been selected
again, the constraint is resolved.
Figure 9.14.: Deleting of coerce above an unrefined node
(a)
Before
wrapping
(b)
After
wrapping
Figure 9.15.: Before and after wrapping with w wrapper 2
9.3.5. Wrapping
Introducing coerce at the beginning is easy. What’s harder is how to deal with it later
on. Wrapping, the insertion of a fun between another fun and one of its arguments, is
such a case. For wrapping to work, a fun has to return the same cat, as it takes as t
least one of its arguments, as with B in
f u n wrapper : A1 -> ... -> Bk -> ... -> An -> Bk ;
wrapper can be inserted between at a node of type B and its parent, whereas the subtree
beginning in that node will be used as the kth argument. This example (with k=2) is
shown in figure 9.15.
CoercedTo
With the first-mentioned tagging approach using CoercedTo, funs will return Instance,
but expect CoercedTo. And wrapping is only possible for funs, which take the type as at
least one of their arguments as they return, since otherwise, the fun won’t fit in there.
And that is not the case here. GF will not offer wrapping commands then, making
bottom-up editing effectively impossible.
Wrapping with two funs at the same time is not supported by GF, so no sneaking in
72
9.3. Coercions
with another coerce is possible here. So the to-be-wrapped subtree has to be cut to
make GF present the full refinement menu of all funs fitting here. And not only those,
which can indeed take the cut subtree as an argument. Thus, stripping out all funs that
cannot accept this subtree would be up to the editor. In pseudo-code, that could be
done as follows (commands chained with ’;;’ can be sent in one step):
add the current subtree to the clipboard
;; delete it
for all funs ’f’ in the refinement menu
refine with ’f’
for all arguments ’a’ of ’f’
if type(’a’) == Instance ?
//coerce will be applied automatically
move to coerce’ Instance argument
;; paste the cut tree
;; ask GF to solve constraints
if the Subtype witness is filled in
&& the cut subtree can be pasted here
prepare a chain command
undo move, refine, solve, move
undo refine
undo delete
add these chain commands to the refinement menu
These chain commands would consist of adding to the clipboard, deleting, refining with
the new wrapping fun (coerce will be applied automatically by GF), pasting the cut
tree, and solve to fill in the rest of coerce’ arguments. If the user does not use such a
chain command, then the editor has to delete the cut tree from the clipboard to clean
up again.
This version of the algorithm allows only wrapping with the same type, not with a subor supertype, but is already quite slow:
Iterating through all funs and their arguments is linear in the number of funs. Approximately, every fun has two arguments and every second is an Instance. So for every fun,
1 call to GF for the initial refining and 1 call for each argument has to be made, together
with parsing the returned state. The undos can be chained in front of the next command
since the returned state after it is ignored anyway, so they do not cause additional calls
to GF.
The current version of the editor does around 6 calls to GF in the background and even
with a small linearization, where appending and rebuilding the HTML does not hurt
that much, a small slowdown is noticeable. An normally, between 20 and 30 funs are
offered by GF for an Instance FixedType. So doing 50 calls to GF in the background
would become quite annoyingly slow.
Therefore, CoercedTo has been discarded and tagging is done with printnames.
73
9. Usability Improvements
Tagging with printnames
With tagging using printnames, wrapping will still be possible, since funs still return
and expect Instance. So wrapping with funs, that go from Instance A to Instance A, is
possible without restrictions, since they are directly offered by GF, as figure 9.16 shows.
Figure 9.16.: Wrapping without CoercedTo is still possible, as long no subtyping is involved.
But when it comes to wrapping with sub- or supertypes, things get complicated too. In
fact, since there too will be a coerce between wrapper and wrappee, the problem is quite
identical for both approaches.
The following parts in this section have not yet been implemented, but at least with this
tagging approach the user can wrap, if the type stays the same, something that wouldn’t
be so easy and fast with CoercedTo.
The general case for wrapping with subtyping is, that the type constraints on the wrapper
fun are loosened. It does not need to have exactly the type which the original parent fun
expected as an argument, a subtype of that is enough (which might be even a subtype
of the type of the former child fun). Analogous, the wrapper fun is not restricted to
expect the exact type of the former child fun as its argument, a supertype suffices.
In a slightly simplified, but not less general example, this looks as follows: Given classes
C B A and funs aa : A -> A, bb : B -> B and c : C. With implicit coercions,
an initial tree (aa c) and a target tree (a (bb c)) is given. And with explicit coercions,
74
9.3. Coercions
(a) The initial
state
(b) The target
state with bb in
between
Figure 9.17.: Example situation where a wrapper fun is to be introduced, that neither
as the child nor as a parent fits without coerce. The actual coerce nodes
are hidden.
(a) The initial state
(b) The target state with
bb in between
Figure 9.18.: The situation from figure 9.17, but with coerce shown
this would look like (aa (coerce C A C2A c)) in the beginning, and (aa (coerce B A
B2A (bb (coerce C B C2B c)))) in the end. Figures 9.17 and 9.18 display the same
example as graphical trees.
As stated above, GF will only offer the funs for ordinary wrapping. To add the others
into the refinement menu is up to the editor, which has ask GF for them. This can be
done as follow:
add current node to clipboard
move up
;; delete the coerce above
;; reintroduce it
;; move to the Instance argument
for all now offered funs ’f’
refine with ’f’
;; ask GF to solve constraints
if Subtype argument of coerce is filled in
for all arguments ’a’ of ’f’
if type(’a’) == Instance ?
75
9. Usability Improvements
move to ’a’
;; refine with coerce
;; move to Instance argument
;; paste the cut tree
;; ask GF to solve constraints
if the Subtype argument of the coerce above is filled in
prepare a chain command
undo solve, paste, move, refine, move
undo solve, refine
undo move, refine, delete, move
add these chain commands to the clipboard
Here, the chain command would consist of add tree to clipboard, move up, delete coerce,
reintroduce it, move to Instance argument, refine with f, move to a, refine with coerce,
move to Instance argument, paste from clipboard, clean up clipboard, solve constraints.
And, like above, if another command is sent to GF, clean up the clipboard.
A version of this algorithm for CoercedTo wouldn’t be very different. Just that coerce
would get refined automatically, but otherwise it would be the same. And it can replace
the algorithm above since the funs that wrap from A to A are included, too.
Additionally, this algorithm is not slower. Again, one initial call to GF per offered fun,
and then one call per Instance argument. Also undos can be treated as above. But the
speed problem would still be there, since ‘not slower’ does not mean ‘not slow’.
For Instance, probably most wrapping will be done with the classes IntegerC, RealC and
BooleanC. And except for RealC, these classes have no subtypes, so that the wrapping,
that GF offers, is enough. And for the rest, a button could be added, so that this
procedure is only executed on demand.
Or in another thread in the background, that works, while the user is thinking. For this
to work, the algorithm has to be interruptible at any stage since it has to share the same
GF process with the normal editing commands because GF can need over 180 MB of
memory and can take 20 s to load a grammar and thus, no further instances of GF in
the background are really possible. Since every cycle of the inner loop of the algorithm
is quite short, it would be enough to just let it run through and check at the beginning
of the loop, if a special termination flag is set. If yes, execute the undo of the outer loop
and the one for the initial commands and then terminate that thread. That way, these
commands could be added one after each other to the refinement menu, without making
the user wait too long, if he doesn’t need them.
9.3.6. Changes afterwards
Another problem with always hiding coercions is, that if the user changes type arguments
on which other arguments depend, and these arguments are already refined, there will be
a clash. And to fix that clash, one has to see its source, and for that all type arguments
76
9.3. Coercions
have to be displayed. And coerce with its two type arguments is a likely candidate for
such a clash. The implemented way to deal with unsolvable constraints is to display the
coerces to make the user know, what the type arguments for the coerces are.
Also, whenever one of the ancestors of a node has such a constraint attached to it, the
node cannot be hidden, as visible in figure 9.19. Then a user at least has a chance to
spot the error. The constraint, as given by GF will just say something like Instance
Figure 9.19.: The type argument of eq has been changed which lead to a GF constraint.
To help in resolving that, the editor displays all affected coerce.
Real <> Instance Integer, but will not tell where the conflicting nodes are. Finding
that out would impose type checking on the editor, which by the design should be done
by GF alone. Therefore, the display of constraints has not been touched in this work,
although it would be helpful for the user to improve it.
But there are cases with subtyping errors that GF does not find. If a subtyping witness
cannot be filled in, GF will say nothing. The node cannot be refined, but that’s it for
GF.
So the editor will mark those nodes red, to signal to the user, that there might be a
problem there. An example for this can be seen in figure 9.20.
9.3.7. Collection subtyping
In OCL 1.5 there are no nested collections. But the standard is very vague and underdefined when it comes to flattening nested collections. Everything, that is said about them
is the following paragraph (quotation from the OCL 1.5 standard2 , chapter 6.5.13):
Within OCL, all Collections of Collections are flattened automatically;
therefore, the following two expressions have the same value:
Set { Set {1 , 2} , Set {3 , 4} , Set {5 , 6} }
Set { 1 , 2 , 3 , 4 , 5 , 6 }
Nothing is said about nesting different kinds of collections, like with Sequence(Set(
OclAny)), where the elements of the inner Set would have to be ordered somehow to
2
http://www.omg.org/cgi-bin/doc?formal/03-03-13
77
9. Usability Improvements
Figure 9.20.: The editor noticed, that a subtyping witness is missing
become members of a Sequence. To side-step the flattening problems, the OCL grammars from Kristofer Johanisson avoided flattening collections and allow for nested ones,
since in OCL 2.03 they will be allowed. Yet, that brings up a problem with subtyping
witnesses.
If a Subtype argument is not refined, that does not necessarily have to be a subtyping
error. Sometimes GF does not fill in subtyping witnesses automatically, even if there is
only one possible refinement. And at the moment, the editor does not check that automatically. For non-collection subtyping this normally does not occur, so that shouldn’t
be a problem there.
For Collection and its subtypes in OCL things are a bit different. The subtyping witness
for them looks as follows:
setConforms2coll : ( sub , super : Class ) -> Subtype sub super ->
Subtype ( Set sub ) ( Collection super ) ;
A Collection(Real) in the grammars (and in OCL 2.0) is a supertype of Set(Integer).
There are two subtyping relations involved here. setConforms2coll as such is for the first
one, that Set is a subtype of Collection. But then there is a nested call for to fun with
return type Subtype, since something like Bag(Set(Sequence(Integer))) is a subtype of
Collection(Set(Collection(Real))), and that could get as deeply nested as it wants.
3
OMG Final Adopted Specification: http://www.omg.org/docs/ptc/03-10-14.pdf
78
9.3. Coercions
The algorithm to fill in the Subtype witnesses ‘by hand’ by the editor is as follows: First,
ask GF to solve any constraints, where an open node must have the same type as a
refined one. But as mentioned above, that does not always work. Then scan the AST
from the root to the leaves in its textual representation for open nodes of type Subtype,
where the two nodes above are already refined (this constellation is the same for coerce
as for the collection subtyping witnesses). If one of the two nodes would be open, then
it can’t be decided yet, if there is a subtyping problem or not. Now, move the focus
to a unrefined Subtype node where sub and super are defined and take a look at the
offered refinement menu. If it contains just one entry, refine with that. If no refinements
are offered, we know that there is a subtyping problem4 . More than one refinement is
impossible, since for each pair sub and super, only one subtyping relation exists. As
the next step the editor has to look at the tree and move to the next node as described
above.
Here, in contrast to what was noted above, some knowledge about the tree is preserved.
The tree is not rebuilt, since just its textual representation is scanned, and rechecking
the same first undefinable node again and again is prevented with a line counter. The
check is directed strictly downwards. This works, since only such nodes are touched
where sub and super are already refined. With that precaution, no nodes above will
notice any changes, since they can at most rely on those two arguments, but never on
the Subtype argument.
This procedure is, at the moment, not run automatically for speed reasons and because
nested collections are supposed to be quite rare. There is a button Close Subtypes,
which executes it.
9.3.8. Summing up coercions
The goal of making coercions completely implicit as they are in OCL has not yet been
reached. Just in some cases when deleting or changing type arguments, the user will
catch sight of them. But as long as the user doesn’t change his mind and wrap funs,
he shouldn’t have to do anything with them. For example, the tree in figure 9.11(b) is
done with the current implementation. And no coerce was visible during the creation of
the tree in that screenshot, either.
One problem, that sometimes remains, is that the refinement menu contains too many
entries. funs depending on type arguments will always be offered, regardless if the type
arguments can be filled in or not, which for speed reasons is not checked.
4
Note, that for later on this knowledge gets forgotten, since the editor holds no knowledge about
previous states of the tree when the tree is rebuilt.
79
9. Usability Improvements
9.4. Minor Improvements
9.4.1. Suppression of self and result
To remove the superfluous self and result depicted in section 7.11, the editor refines with
them in the background, moves the focus to the VarSelf argument and checks, if that
position has been filled in automatically, and undoes that refinement afterwards. If the
argument was filled in, self respectively result are left in the refinement menu, otherwise
removed.
This works, as long as the type of the expected Instance is fixed. Then the type argument
of self will be constrained to be that class, and only then GF checks, if there is just one
possible refinement for the VarSelf argument. If there is, the editor will show self in the
refinement menu, otherwise the editor will hide it.
But when the expected type of Instance is not fixed (as for example below a coerce where
several subtypes are possible), the Class argument of self won’t be filled in in the first
place. That leaves the scene open for the VarSelf c guard attribute. This can now be
filled in, which triggers the refinement of the Class argument. So now, all arguments of
self are refined. And that is the case in any OCL context, regardless of the type of self.
That is also the case for result, if the context is a method contract of an operation that
does return a value. And since self could be completely refined, it will be shown in the
editor 5 .
This is OK as long as the expected type of self does not have to be a subtype of another
type. But in cases where self has a type which is not a suiting subtype, self would still
be offered, since for GF the lack of a suiting subtyping witness is no problem. So in case
the VarSelf argument is refined the editor checks, if this self is the Instance argument of
a coerce. If it is, it will also check the Subtype argument and if that is not filled in, it
will hide self. The Subtype argument is automatically refined, since there can only be
one. That for Collection GF sometimes does not close the Subtype argument is hardly
ever a problem: self will never have a Collection type and result only in the rare case
mentioned above.
Recapitulating, self and result are (nearly) only offered, when they are applicable, otherwise they do not show up in the refinement menu.
9.4.2. Easier access to properties of self
Users who have been shown the editor, missed easy access to properties of self. If
properties are accessed, these are most probably properties of self, since that is the
instance for which the constraints should hold. So they wondered why these could not
5
Note, that when the Class argument gets refined, that can imply for GF that other unrefined Class arguments that depend on it get refined, too. But the undo command will also undo these refinements,
so that won’t mess things up here.
80
9.4. Minor Improvements
be accessed directly.
That can be done now. Whenever an Instance is expected, the editor refines with a
property call for self in the background and adds all funs that GF offers there as chain
commands to the current refinement menu (see figure 9.21 for an example). These chain
commands consist of refining with implPropCall, the generic property use fun that omits
self in the output, a move to the receiver argument, the refinement with self, a solve
to tell GF to fill in possibly still open type arguments, a move to the actual property
argument of implPropCall, and finally, the refinement with the chosen property.
Figure 9.21.: Direct access to the suiting properties of self, here for Integer
The problem with this approach is similar to the one mentioned in section 9.4.1. If the
expected type is not yet fixed, GF will offer all properties of self. If that later on will
lead to a clash is not checked.
Therefore, whenever the focus is below a coerce where the subtype argument is not filled
in and the supertype is not OclAnyC, the refinement menu for the properties of self is
calculated differently. The editor will refine with all possible subtypes as in section 9.3.3
and then collect the refinements for each subtype as above. The list of refinements
collected this way will not contain properties which do not have a suitable subtype.
Only this list is used for creating chain commands like above, which are then presented
to the user.
But this checking involves a number of calls to GF that are only needed, when such a
property is to be accessed. Therefore, the check in the background is deferred and a
special bogus entry for the self is listed in the refinement menu in the beginning. Only
81
9. Usability Improvements
when the user clicks that, this submenu is computed.
There is one complication for the creation of the chain commands, namely when only
one property is available at all for the given supertype. GF will then refine with that
automatically. So the editor has to check after trying out all possible subtypes, if only
one refinement was offered for all subtypes (a delete will always be sent to GF at the
position, where the properties will show up to make single refinements appear there
also). If that is the case, a modified chain command will be offered to the user, which
does not contain the actual property, since refining at a already refined AST node will
produce an error from GF and must be avoided.
9.4.3. Comparison operators
Comparisons for Integer
The comparison operators for Integer have been removed. With the transparent handling
of coercions these are not needed anymore. The refinement menu for the operands will
only contain funs of type Instance IntegerC or Instance RealC6 and coercions are hidden.
With that, the reason for separate Integer comparisons is gone.
Equalities
Since with eq there is a general equality, that can play the role of any other equalities,
these are superfluous. For the model classes never specific equalities existed, so there
was an inconsistency and the user had to choose either anyEq or eq anyway for them.
Now all equalities except eq and its counterpart neq have been removed. These can
handle all others.
But eq is not perfect, it too, has its problems (which would be the same for the specialised
equalities), namely when subtyping comes into play. eq takes a class c, on which the
comparison should take place, as an argument on which the two others depend:
f u n eq : ( c : Class ) -> (a , b : Instance c ) -> Sent ;
But one of the two instances may have a subtype of c. The dynamic type of the instance
of c may stillbe the subtype, so that equality indeed is possible. OCL forbids equality
on different types, so if there is no inheritance relation of a and b, this is incorrect OCL.
There are no explicit upcasts in OCL allowed, so that cannot be stated in OCL. But
nevertheless, the current eq allows such a statement.
In a discussion with Aarne Ranta, a new equality operator came up:
c a t EitherSub Class Class ;
f u n eitherEq : (c , d : Class ) -> ( e : EitherSub c d ) -> ( a :
Instance c ) -> ( b : Instance d ) -> Sent ;
6
An exception for that are properties, there still all will be listed (see section 9.4.2).
82
9.4. Minor Improvements
f u n s ubt yp eF ir st Se co nd = (c , d : Class ) -> Subtype c d ->
EitherSub c d ;
f u n s ubt yp eS ec on dF ir st = (c , d : Class ) -> Subtype d c ->
EitherSub c d ;
This equality is a variant of Conor McBride’s John Major equality ([McB00]), which
only takes two class arguments, but no subtyping relation argument, so everything can
be compared with this equality, but still only the same thing is identical to itself.
In contrast, with eitherEq it can be enforced that either c is a subtype of d or vice
versa. The type EitherSub Class Class has only two constructors which take the same
Class arguments as eitherEq. Each of them takes care of one direction of a possible
inheritance between c and d. But since there are two of them, GF will not refine them
automatically. Thus the editor would have to refine with both of them, each time giving
the class arguments of eq to them. Since for every two classes there is at most one
subtyping witness per direction, GF will refine that argument automatically if there is
one. If it didn’t do that for both subtypeFirstSecond and subtypeSecondFirst, the editor
can conclude that there is a subtyping error and signal that to the user.
Handling equality without hidden coerce is less error-prone since less type dependencies
are involved. But on the other hand, the user will not want to deal with EitherSub
himself, so replacing that argument with a dummy node that either says ‘subtyping
correct’ or ‘subtyping incorrect’ will be enough for him.
If GF reported for constraints,where they occur, the editor could notice, when the user
has changed one of the Class arguments of eitherEq, and as a result, delete and try
to re-refine the EitherSub argument to see, if the new Classes are valid arguments for
eitherEq.
This approach came up shortly before the end of this work and hence, was not implemented. This would encompass a changed treatment of the fun eq and would make
changes in the OCL parser necessary, since the parser would have to produce a different
output.
9.4.4. Escaping hidden arguments
As mentioned in section 7.3, one could not change the focus position with clicking at a
new position in the linearisation itself, when the currently selected node was not shown
in the linearisation. The author couldn’t find a reason, why that was more or less
explicitly disabled. So this limitation was removed, and now the click-in functionality
of the linearisation display in pure text as well as in HTML works regardles of whether
a visibible or a hidden node is currently selected.
83
9. Usability Improvements
9.4.5. Changed selection colour
In the previous version, the parts of the linearisation, that belonged to the currently
selected node in the AST were coloured green. A colour which normally signalises, that
something is correct, and not incorrect. Which, in a sense, is indeed the case. When no
GF constraint exists at the currently active node that does mean, that there are no type
errors. Thus, the colour green was chosen. In contrast, when there were GF constraints
present, the respective part of the linearisation, that belonged to the constrained nodes,
was coloured red, not only the currently active part. And then, the user couldn’t see,
which part was selected.
That there are no typing errors should be standard and nothing special. So to colour
that especially is not justified, in only makes the user think, that something is special,
although it isn’t. Thus the standard selection colour of Swing is now used instead, since
selection is the thing, that is important for the user, not correct typing. Only when GF
constraints are present, a different colour is chosen. Then, the active constrained node’s
linearisation is highlighted in orange to make it distinguishable from the not selected
parts, which are in red.
Take the lights in a car dashboard: they are off, when everything is alright, no green
‘lights’ should be on, unless something important has to be signalled to the user. The
selection colour, a bluish violet, is the same as in all Swing applications, so text in that
colour it should be recognisable as selected text, not as otherwise specially marked text.
The selected node in the AST was always coloured in the Swing selection colour, and
since node and its linearisation belong together, that shouldn’t be veiled by different
colours. Note, that this is violated if constraints are present. Then the selection colour
in the tree is still the default one. That stands to be fixed.
9.5. Generic Improvements
The following improvements have in common that they are not of big use for editing
OCL constraints. But since the editor was also released as part of GF 2.3 and is expected
to be used with normal GF grammars, some improvements have been done for them,
too (besides the ones already mentioned that are not exclusively for OCL).
9.5.1. Middle-click parsing
The old editor featured the parsing via middle-click in the linearisation area. After such
a click, a small window would appear, where the user could input a text that GF should
parse. The linearisation of the last displayed language is displayed in this field.
This had two problems. When the current node was open, something like ?4 was filled
in there. And the built-in GF parser only can parse question marks, if they are not
84
9.5. Generic Improvements
followed by a number7 . So if the user wanted to give an expression containing open
subtrees, he was given a false hint.
The other problem defied the sense of multilingual editing. The last displayed language
could be one, that one does not understand. But virtually, he does not need to understand that one, since it will have the same content as the other displayed languages, and
he might just read that linearisation instead which is the point of multilingual editing.
But now the user often cannot just perform a little change in the displayed text, since
it might be in an unknown language (for him) like in figure 9.22.
Figure 9.22.: The parsing window called with a middle-click shows the refinement of the
subtree of the current node in the language that GF last sent to the editor.
To remedy this situation, the editor now saves the language for the linearisation snippets,
so that the user will always get the text in the language he clicked on, as visible in figure
9.23.
So now he can choose the language to be one he knows. If the node is not yet refined,
the window will be empty. Just giving a question mark to be parsed, may be possible,
but is pointless, since it brings nothing new with it.
7
Which is an acknowledged bug of GF.
85
9. Usability Improvements
Figure 9.23.: The parsing window called with a middle-click shows the previous text in
the language the user clicked on.
9.5.2. Ask to save before quitting
In the old editor, when the user quit the program, it just quit. It didn’t ask if the
user might want to save the tree or linearisation. So if that happened accidentally, all
unsaved work was lost.
In OCL mode, the situation was the opposite. The constraints were always saved,
whether the user wanted it or not.
Now a message box pops up and asks (see figure 9.24). Also the quit can be cancelled.
This feature is standard and there is no reason why the GF editor should differ here.
9.6. Unimplemented ideas
9.6.1. On AtomSent
The problem with AtomSent in section 7.7 has not been solved in the implementation of
this work. A work-around could be to offer all AtomSents in their positive form as Sent,
and hide the category AtomSent. That way, one would lose the nicer negated form, but
it would make these predicates accessible at all.
It could be implemented either with duplication in the grammars, where there will be
86
9.6. Unimplemented ideas
Figure 9.24.: A simple question box asks, whether the user wants to save before quitting.
just the extra form as a Sent. This would increase the memory footprint of GF a bit. Or
the editor asks GF at the beginning for a list of all funs, that return AtomSent, which
can easily be done by starting anew with the cat AtomSent. And since posAtom does
not depend on other parameters like the AtomSent itself and needs no bound variables
in the AST, this list will never change unless the model gets changed, but that is not
supported while the editor runs anyway. So this list could be cached and added to the
refinement menu for Sent as its own subcategory. This would only increase the startup
time of the editor a bit, but shouldn’t impede all further usage with regard to speed.
But this is not yet implemented and the current way to deal with it is that the subcategory for AtomSent in the refinement menu has the following as a tooltip: ‘boolean model
elements and non-standard OCL statements like ’an exception is thrown”. This is no
real solution, but at least gives the user a slight chance to discover where boolean model
elements are hidden. A better way is definitively desirable, but since there is ongoing
work on negated forms in the resource grammars, there is light on the horizon for a real
solution without work-arounds.
9.6.2. The collapsing tree
The problem of section 7.12 is so by design, which is, that GF gives the whole state to
the editor, and the editor just displays that, but keeps no knowledge of previous states
by itself. The question is, if the design is good, especially with regard to this problem.
87
9. Usability Improvements
What is to be bridged, is the independence of different GF calls on one side, and the user,
who expects, that his decision to collapse or unfold tree nodes should be remembered on
the other side. For the user, the tree will still be the same, just with the changes from
the last command applied. But for the editor, the tree always is completely new. Thus,
there is a mismatch between the user’s mental and the program mode. And in such
situations, the program model should be adapted unless there are important reasons to
make the user rethink. But that is not the case here.
Please note, that the following ideas have not been implemented.
One way to help here would be a weak memory of the previous state. Weak memory
means, that a tree node is reused, if the node in the GF AST is the same (perhaps
modulo selection) as the one from the previous GF run. Then the status in the graphical representation would be remembered, but the fun would still be the same. So an
expanded node could survive as expanded, a collapsed node would be collapsed. And if
the node has changed, the editor would recreate all child nodes. Which is not ideal, but
still much better than the current state. Otherwise the editor would have to anticipate
changes in the tree or do a tree diff. Which was not thought out in this work.
Alternatively, when a mp command (move to another AST position) has been issued
before, just update the selection status in the tree nodes, that represent the GF AST
nodes that now have a changed selection status, and make the new node visible and
selected in the graphical representation, while removing the selection flag from the old
one, but without changing its collapsing status. But do not rebuild the graphical tree at
all. Parsing the GF tree can not be avoided, although the mp can be intercepted and the
new position read from the command string, since depending on the currently selected
node and its position in the tree, a new command could be issued to GF automatically
(see the section 9.3). For the user, moving in the tree does not change it. And this
approach respects that.
88
10. Implementation
10.1. Refactoring the old version of the editor
The editor originally was meant as just a graphical interface to GF which ran as a standalone application. Giving the editor more intelligence to help the user with OCL made
it necessary to use many parts of the editor in new ways. For that, the old code had to
be refactored to suit the new requirements.
10.1.1. Documentation
Before The code contained no JavaDoc comments and only very few other comments.
Over all, there were ca. 4 comment lines per 100 non-empty source code lines and the
former developer was on a leave. What methods do and attributes are used for, was
nowhere described. And just reading the code didn’t help that much, because with GF
there is an external program, whose output is processed, output which is not readable
in the source code. And since there was no documentation about the protocol that GF
and the editor speak with each other, the running system had to be analysed with a
debugger.
Also there was no documentation about the control flow. The main event loop, for
example, was in the constructor of the GUI class and thus, the constructor was never
finished. Since the GUI was already initialised at the point the main loop was entered,
the user could start to use the editor and issue commands to GF. Those were sent
via STDOUT from inside the Event Handler Thread to GF, which was started by the
constructor thread. This external program then sent its output to the STDIN of the
GUI where the main loop in the constructor thread parsed the output and updated the
GUI elements of the editor window.
Not knowing that the constructor was never meant to finish and only having this implicit
multi-threading which was mentioned nowhere, made debugging quite complicated at
the beginning. Sending and reading were not connected in the Java code, so stepping
through the methods just ended after sending.
Now In contrast to the state before now every class, method and nearly every attribute
has a JavaDoc comment attached to it. Also in the protocol parsing parts of the code,
comments tell what is read in a line or why a branching is necessary. Overall, there are
89
10. Implementation
now 2224 lines of JavaDoc and other comments for 5928 non-blank and non-comment
lines. So instead of 4 comment lines per 100 real code lines as before, there are now 38
per 100.
10.1.2. Names
Before Some local variables had names like j, j2, k and were defined at the beginning
of a method without any hint to which purpose these variables serve. The use of these
variables depended very much on the input data from GF, so just by looking at the code,
it was not possible to find their use or what values were stored there. Only debugging
the programs helped here.
Some methods had names, that were too general and did not really say, what the method
is actually doing. So here too, without documentation and due to the methods’ dependency on the output of GF only debugging helped here.
Also a number of attributes had the problem of having a name too general. These
attributes lead to the next point in section 10.1.3.
Now Names like j, j2, k do not occur anymore in the code, those variables got speaking
names. Exceptions to this are integer loop variables and some String parameters called
s, where the method name already makes the usage clear. And a number of critical local
variables additionally got comments that describe their usage.
10.1.3. Data flow
Before The old version of the editor uses 23 attributes as global variables whose value
does not need to be saved. They are used instead of return values and parameters for the
purpose of parsing the XML from GF and displaying tree and linearisation. Mostly they
have been used in several methods which interacted on them. The main methods for
parsing and displaying interact purely with side-effects, they take no parameters, return
void, and several of them also affect the GUI. Together with the lack of comments, it
was very hard to figure out which purpose those variables served. Again, only debugging
with real input from GF helped here.
Changing the order of the method calls would also give unexpected results due to their
interactions, but would be needed when, for example, calling GF in the background to
check something.
Now There is one attribute left in the main class, which could perhaps be made local,
and one in the class that handles the linearisation. All other global variables have either
been removed or transformed into local variables and parameters. Thus it was possible
to factor larger parts out of the main class. This is described in section 10.1.5.
90
10.1. Refactoring the old version of the editor
10.1.4. Static attributes
Before The editor was meant to be a stand-alone application and as that it didn’t
matter if an attribute was static or not, since only one instance of the main class was
running at a time. So most of the attributes that kept the state of the editor were just
static without a special reason to be so. But in the context of TogetherCC where one
might want to start the editor more than once for several classes/methods at the same
time, it does matter.
Now Only the loggers and final attributes are static, no more attributes that are used
by just one instance of the editor. Additionally, the main reading loop from GF has been
removed from the constructor. That way, the main thread, in which also TogetherCC
runs, is no more blocked and the editor can be started as several instances at a time.
10.1.5. Division of labour
Before The editor consisted of just two main classes, one for the general GUI and one
for the graphical representation of the AST. Additionally eight more or less anonymous
classes were used as callbacks. But except for the tree display class, all functionality was
gathered in just one big class with no further encapsulation, which hurts the Principle
of Single Responsibility as described in [Mar02]. All steps in getting the state from
GF as text, parsing that text, creating the internal data structures, updating the GUI,
receiving commands from the user and sending them to GF were done in this class.
In addition, the main class implemented the listener classes for all GUI elements except
for the AST. So the event handler methods consisted of huge case distinctions to figure
out, where the request came from. This isn’t inevitably bad, but having 24 cases, from
which only 1 is executed at a time, with handling code up to over 50 lines, makes this
harder to read than necessary.
Now As stated in section 10.1.3, the main class has been split. All direct reading/writing from the main class is now moved to an encapsulation class. This class not only does
the reading, but also some parsing. Thus reading is a completely separate step from
forming the GUI, which was not the case before.
The same goes for parsing the <linearizations> element of the GF XML. The linearisation is formed and the indices are produced without affecting the GUI.
The whole management of the refinement menu is now in its own class, including the
graphical representation of it. That way, the editor is oblivious to the fact that there
are now submenus. Also the context menu of tree and linearisation is produced there,
the main GUI just asks the refinement menu class for it.
The tree display has already been in a different class. Hence, only some minor decoupling
was done here, but these two classes still have to play together.
91
10. Implementation
The architecture is not (yet) Model-View-Controller or Model-View-Presenter or any
of the other separation patterns. The main GUI class does the choreography of the
reading from GF and display, it does not just get notified/updated, when the model has
calculated everything. Additionally, although the main editor class is no longer a listener
class, this work has only been moved into inner classes, but not into a separate presenter
or controller class. With the mix of reading from GF and GUI forming removed, it
shouldn’t be too hard to change it, but for now, the view just does too much.
The reason for this lies in the fact that this work didn’t start from scratch, but with code
that was far more off from MVC than now, and the author proceded with the maxim
‘refactor the low hanging fruit first’1 . Gradually the different steps from reading the
XML over parsing it and forming the GUI have been disentangled, but more basic faults
like the ones listed above were much more important and hence, had to be corrected
first. With them, the system is now in a much better shape than before, but, as stated
above, not yet complying to one of the more advanced GUI patterns.
10.1.6. Character counting for click-in functionality
Before A less general problem, but one that was affected by most of the problems
stated above, was the calculation of the indices of the individual linearisation pieces
in the linearisation area. Each was stored together with its position in the AST as a
MarkedArea. The indices were calculated in roughly the following way (modulo some
special cases): When a subtree tag in the XML from GF was found, the character
positions from its beginning and end in the XML string were saved together with its
associated AST position. Then the subtree tag was removed and the two saved values
were modified to take the characters now missing in the XML string into account. Thus
the beginning and ending positions were always relative to the (modified) XML string,
not to the displayed text.
For the display of pure text, this can be done. Just that this method cannot be carried
over to HTML. Here the source code length cannot be used as a measure in the display
when it comes to caret positions. Code like ‘<b>I’m bold</b>’ will just show up as
‘I’m bold’ where the tag is not visible and thus not noticeable with regards to caret
positions in the output.
This method is not applicable to HTML and has to be replaced. Character counting in
the output is needed.
Now Only the length of the text of one linearisation sniplet when it is displayed,
matters, but not how many characters are used for it in the XML. This is described in
detail in section 9.2.
1
http://c2.com/cgi/wiki?RefactorLowHangingFruit
92
10.2. GF’s XML
10.2. GF’s XML
The editing state is sent as XML from GF to the editor. A (shortened) example of this
XML can be seen in figure 10.1. This XML is not well-formed, because the values of
attributes are not enclosed in quotation marks. But since the parser was not a generic
XML parser, but a simple parser specifically for GF’s output, that did not matter.
And an XML parser couldn’t be used, because the highlighting in the GUI depended
on the exact character positions in the XML strings, which would be lost in the tree
representation of an XML parser. In principle, the same parser is still used in the current
version, although it shouldn’t be too hard to change that, since this dependency has been
removed.
10.2.1. <hmsg>
The first element of the <gfedit> tree is an optional message. If a command sent to GF
is prefixed with something in square brackets like in [t] gf command, GF will repeat
the content of those brackets in the hmsg tag. This is used to transfer some flags to the
next GF run. t for example tells the editor to rebuild the tree. Some commands to GF
do not alter the editing state and therefore do not need a tree rebuild, like switching
linearisation languages on or off.
10.2.2. <linearizations>
The second subtree contains the linearisations in the active languages. Each of them is
in a separate <lin> element which has the name of the language as an attribute.
The tree in abstract syntax is always given to the editor, but normally just ignored.
But the abstract tree in this format can be saved and loaded with GF2 , while not all
linearisations are parseable again. Except for that, the linearisation in the abstract
syntax is ignored since the tree also has its own XML element. All other languages the
user wants to see come after Abstract.
Here are <subtree> tags, that are nested the same way as the AST is, with the difference
that hidden arguments will not show up in the linearisation. funs, that solely rely on
the linearisation of one of their children like coerce will not appear here either. As
attributes, the <subtree> tags contain the type and AST position of the correspondent
AST node. If there is a GF constraint for a node, an additional attribute ‘status’ with
value ‘incorrect’ will appear inside the <subtree> tag.
For the parts of the linearisation that belong to the currently selected node3 , a special
2
With the exception of metavariables. In the abstract syntax from GF they are numbered, but the
parser cannot parse them with numbers. So when saving as the AST, the numbers are removed.
3
There can be more than one of them for discontiguous constituents, that is, a lin can use one part
of the linearisation of a child, then something from another child and then something from the first
child again. If now this first child is selected, its linearisation will not be contiguous, whereas the
93
10. Implementation
<g f e d i t >
<hmsg>
t
</hmsg>
<l i n e a r i z a t i o n s >
< l i n l a n g=A b s t r a c t >
NOPACKAGEP PayCardC charge IntegerC Oper Constr
( \ t h i s , amount −> p r e p o s t C t
( realGT
( coerce
IntegerC
RealC
IntegerCConforms2RealC
amount
)
?9
)
?2
)
</ l i n >
< l i n l a n g=FromUMLTypesEng>
<s u b t r e e p o s i t i o n = [ ] t y p e=C o n s t r a i n t > For t h e o p e r a t i o n \<b\> c h a r g e ( amount :
I n t e g e r ) \</b\> o f t h e c l a s s \<b\> PayCard \</b\>, \<br\> g i v e n t h e f o l l o w i n g
pre−c o n d i t i o n : \< u l \> <s u b t r e e p o s i t i o n = [ 0 ] t y p e=OperConstraintBody> <
s u b t r e e p o s i t i o n = [ 0 , 0 ] t y p e=Sent> <s u b t r e e p o s i t i o n = [ 0 , 0 , 0 ] t y p e=I n s t a n c e
RealC> <s u b t r e e p o s i t i o n = [ 0 , 0 , 0 , 3 ] t y p e=I n s t a n c e I n t e g e r C > \< i \> amount \</ i
\> </ s u b t r e e > </ s u b t r e e > i s g r e a t e r than <s u b t r e e p o s i t i o n = [ 0 , 0 , 1 ] t y p e=
I n s t a n c e RealC> \< i \> ?9 \</ i \> </ s u b t r e e > </ s u b t r e e > </ s u b t r e e > \</ u l \> then
t h e f o l l o w i n g post −c o n d i t i o n s h o u l d h o l d : \< u l \> <s u b t r e e p o s i t i o n = [ 0 ] t y p e=
OperConstraintBody> <f o c u s p o s i t i o n = [ 0 , 1 ] t y p e=Sent> ?2 </ f o c u s > </ s u b t r e e >
\</ u l \> </ s u b t r e e >
</ l i n >
</ l i n e a r i z a t i o n s >
<t r e e >
NOPACKAGEP PayCardC charge IntegerC Oper Constr : C o n s t r a i n t
\ ( t h i s : V a r S e l f NOPACKAGEP PayCardC) , ( amount : I n s t a n c e I n t e g e r C ) −> p r e p o s t C t
: OperConstraintBody
realGT : Sent
c o e r c e : I n s t a n c e RealC
IntegerC : Class
RealC : C l a s s
IntegerCConforms2RealC : Subtype I n t e g e r C RealC
amount : I n s t a n c e I n t e g e r C
?9 : I n s t a n c e RealC
*
?2 : Sent
</ t r e e >
<message>
</message>
<menu>
<item>
<show>
r andS
</show>
<send>
r o c l L i b r a r y . andS
</send>
</item>
<item>
<show>
r anyEq
</show>
<send>
r o c l L i b r a r y . anyEq
</send>
</item>
</menu>
</ g f e d i t >
Figure 10.1.: A sample of the XML for a GF state. The refinement menu has been
shortened to make the XML fit on one page.
94
10.2. GF’s XML
Figure 10.2.: The editor after receiving the XML from figure 10.1.
<focus> tag is used. It only has a different name, but functions identically otherwise.
Everything in the scope of this tag was marked green in the old editor. Now, the tree is
built first, and the focused node will already be known, so that the <focus> tags will
just be changed to <subtree> tags before the linearisation is parsed. Each <subtree>
tag carries its position in the AST. But more about that later in section 10.4.
The result of parsing this <linearisations> tag can be seen in figure 10.2.
10.2.3. <tree>
This tree is just a piece of some otherwise structured text, but no XML. Only indentation
is used to make nodes to children of other nodes. Therefore the tree parser works that
way to transform that text into a tree data structure. Each line represents a node in
the internal GF AST and will be displayed as a node in the graphical tree, as visible in
figure 10.2.
In that tree, the selected node line has a * in front of it. The position of this node will be
saved for highlighting (see above) and the type will be displayed below the linearisation
area. The tree will later get analysed to find out, if, for example, a coerce should be
introduced automatically.
linearisation of its parent will encompass the linearisation of itself and all its children, so it will be
contiguous.
95
10. Implementation
10.2.4. <message>
This element is most often empty. Its content is not part of the editing state and is
used to give information to the user. This information could be error messages, if, for
example, parsing has failed or he wants to move the cursor to a non-existing AST node.
It is also used to display explicitly ordered information. From the editor for example,
that is used to get a list of all printnames available to GF as mentioned in section 9.1.4,
but a user who knows GF’s internals, can use that too.
10.2.5. <menu>
Each entry of the refinement menu is given by GF in this part. The <item> element
groups together the text in the <show> element that is to be displayed to the user, and
the according GF command for it in the <send> element. If no printname is available,
the <show> part will basically be the same as the <send> part, perhaps with more
speaking commands and type annotation, if Abstract is chosen as the menu language.
If a concrete language is chosen, then the linearisation of each fun will be given in the
<show> tag. If a printname is available, it will be preferred to the linearisation. The
same holds for the extended printnames of this work, except that they are loaded and
cached at the beginning and the <show> tag will be ignored.
10.3. Overview over the classes
To give an overview over the classes in the implementation, how the classes are connected
to each other and what their main function is, is the task of the figures 10.3, 10.4 and
10.5. How they play together is described in the sections below. Record-like casses, that
do not methods, but just contain fields are used to store the result of some methods.
These classes are omitted in the diagrams to make them more concise.
10.4. Receiving the state
The editor has two tasks. The first is to display the state that GF sends to the user.
The second is to transform the GUI activities of the user into GF commands for GF.
But a great deal of task two has to be prepared in the first task, since many interactions
depend on the current state, like the refinement menu’s content.
10.4.1. XML processing
As mentioned above, after every user action, GF sends the complete state to the editor.
After each send, the main class GFEditor2 is given the task to process the state.
96
10.4. Receiving the state
The graphical
representation of the
GF AST.
JPanel
KeyListener
DynamicTree2
ActionListener
ReadDialog
Does not build the
tree itself, just
GUI class that
takes terms and
strings that are
Cut the context
part from the
OCL and save it
to be parsed
from the user
as a JavaDoc
comment
displays it. Also
reacts to clicks from
the user and tells
GFEditor2 to make
GF switch to the AST
node the user has
inTogetherCC.
MyTreeModelListener
−MyRenderer
PopupListener
JFrame
GFEditor2
CallbackClassInv
CallbackPrePost
clicked on.
Maps GF funs to
printnames for the
display in the
PrintnameManager
refinement menu.
PopupListener
OpenAction
SaveAction
ImportAction
NewTopicAction
ResetAction
ConstraintCallback
No dependancy
on the
TogetherCC
OpenAPI.
QuitAction
RandomAction
UndoAction
RefinementMenu
AlphaAction
GfCommandAction
ReadAction
SplitAction
CombineAction
SubtypeAction
LangMenuModel
Stub for saving
OCL constraints
in TogetherCC.
GfCapsule
Encapsulates all
reading from
and writing to
the running GF
process. It is
Linearization
called from
GFEditor2 and
returns the
read partially
Takes the parsed XML for the refinement menu
and displays it to the user. The whole submenu
handling is done here.
Display
parsed XML.
Contains the GUI controls for the refinement menu
and takes care of their display.
The refinement menu can be accessed as the
context menu in the tree and linearization too, and
is built here also.
The main class. Not only the GUI class, but also
controls the other classes around it. With their
help, it retrieves the editing state from GF and
displays it to the user.
Contains inner classes that serve as
ActionListeners
Handles the calculation of the
indices for the linearization
snipplets sent from GF. All
Parses the linearization XML
and manages the mapping
of linearization indices
text that should be shown to
the user is collected here and
(calculated by Display) and
AST positions.
displayed, when the whole
linearisation is formed.
GFEditor2 asks this class
for the AST position, when
Both HTML and pure text are
done in this class.
the user has clicked into the
linearization.
Figure 10.3.: The main GUI class GFEditor2 and the classes around it, that together
form the GUI.
The first action in processing is to ask GfCapsule to read the XML and do some preliminary parsing.
The <hmsg> consists mainly of flags. These flags are parsed and values are set in a
corresponding record class Hmsg.
After that, the <linearizations> element is read and saved. No processing is done with
it in GfCapsule, that’s done at a later stage. The same is done with the <tree> and
the <message> part.
Then the <menu> element is read. The XML structure is quite simple here, so parsing
that is done now. The result is a Vector of the String tuples of command and display
string.
97
10. Implementation
An entry of the
refinement menu,
but not necessarily
Comparable
GFCommand
Is responsible for
the text, the
tooltip and the
a command, that is
to be sent to GF.
submenu in which
the associated
command will
show up.
RealCommand
LinkCommand
InputCommand
Printname
As the name
says, an
object of this
when selected,
the submenu
that belongs to
Exists for Integer and String. If
such a refinement is possible, an
InputCommand will be shown in
class contains
a command
this command
is opened
the refinement menu. If selected, a
dialog box will pop up, where the
string, that is
sent to GF.
user can enter an Integer or a
String, depending on the type of
this InputCommand.
SelfPropertiesCommand
This entry is an oddity here, since it it talks to GF
(via RefinementMenuCollector).
This class handles the deferred gathering of the
properties of self. When it is clicked, it ask GF for
the list of them. After that is done, this entry gets
replaced with a LinkCommand which gives access to
those properties as usual.
An unrefined node will appear with question
mark and type in the tree. But the tooltip will
be taken from the parent according to the
Is responsible for
text and tooltip
and colour of a
position of the child, so that the user can see,
what is expected at this node.
node in the
graphical tree.
UnrefinedAstNodeData
AstNodeData
Represents a parsed line
from the tree, that the editor
got from GF. Here, the type,
GfAstNode
RefinedAstNodeData
A refined node already
has its fun and will
appear with its name in
the used fun, constraints (if
present) and bound
the tree. The tooltip for
this node will also be
variables are accessible.
the one for the fun..
Figure 10.4.: The possible entries of the refinement menu in the upper part, below the
data behind the graphical tree.
98
10.4. Receiving the state
AbstractProber
Uses GfCapsule to return the
refinement menu entries for the
state after a given command.
Can read the XML from GF, but
does nothing with it. Has hotspot
method for the different
elements, so they can get
overwritten if needed.
RefinementMenuCollector
SubtypingProber
SelfResultProber
TypesLoader
PrintnameLoader
Goes
through
the tree
Checks, if
self and
result are
If the refinement
menu entries should
be annotated with
Is used by
Printname−
Manager to ask
and tries
to close all
really
applicable
their type, then
PrintnameLoader
GF for all
available
open
Subtype
at a given
position.
asks this class to
create a mapping
printnames. It
will deliver
between fun name
and type.
them as single
lines.
nodes.
RefinementMenuTransformer
Dependent on
TreeAnalysisResult,
transforms the
refinement menu.
A record class
to store the
result of the
TreeAnalysisResult
TreeAnalyser
analysis
Analyses the tree
and labels the
individual nodes,
for example if they
should be hidden.
Figure 10.5.: The structure of the classes that besides GfCapsule, talk to GF.
Now a record object GfEditResult is returned to GFEditor2.
As the next step, the tree is formed. Every line in the tree string from GF is transformed
into a tree node. Type, bound variables, the used fun and introduced constraints are
saved for each node in a GfAstNode object. Additionally, for each node the position
in the tree in the GF notation is calculated and also stored. For the tree structure,
DefaultMutableTreeNode is used, with AstNodeData as the user object.
The result of this step is then given to TreeAnalyser, which goes through the AST and:
ˆ Labels nodes
– as hidden, if they are a coerce without a constraint. It is marked, which child
node should be used instead.
– as coloured, if they might be a coerce with for a non-existing subtyping relation as described in section 9.3.6
ˆ Saves a reference to the currently selected node
99
10. Implementation
ˆ Finds out
– if attributes of self should be given an easy access, explained in section 9.4.2
– if the refinement menu below a coerce should be reduced so that it only
contains funs that return a suitable subtype (see section 9.3.3)
– if it should be probed, if self and result are superfluous in the refinement
menu as described in section 9.4.1.
– if a coerce should be introduced automatically, discussed in section 9.3.1
The next phase depends on the result of this analysis. If a command is to executed
automatically (at the moment only the refinement with a coerce), then this is done and
the data read in this run is discarded since it will be obsolete when the next state arrives.
On the other hand, if no command is to be given to GF, the processing and parsing of
the current state continues. First, the nodes that have been labelled as hidden, are
removed from the tree and replaced by their designated child. Then the tree is shown
in the GUI. Missing subtyping nodes will be colored red and the tooltips are set for the
individual nodes as described in section 9.1.3.
Thereafter, RefinementMenuTransformer is asked to do some changes to the refinement
menu. These again depend on the result of the tree analysis. The properties of self are
added and the editor tests, if self and result can type-correctly be filled in. If at the
Instance node below a coerce, the refinement menu is built anew with only type correct
entries. Also the delete command is modified then.
Only now RefinementMenu is called to form the menu and submenus, whereas before,
there existed only GFCommand objects in a Vector. Of every subcategory occurring in the
initial list of commands that does not just contain one element, a LinkCommand is created, that, when selected, opens the according submenu. If applicable, an InputCommand
is placed in the refinement menu too. After that, the refinement menu is sorted alphabetically.
The next step is parsing the linearization XML, which is done in Linearization. Whenever a <subtree> tag is encountered, the start and end indices of the current text snippet
belonging to that tag are saved together with the position and the text itself. How the
indices are calculated is described in section 9.2. That way, when the user later on clicks
into the linearisation, the editor can find out on which of these snippets the user has
clicked.
The linearisation text is then given to an object of the class Display, which manages
the linearization areas for text and HTML, and immediately afterwards displayed in
the GUI. That is needed to do the highlighting (explicated in section 9.4.5). For that,
Linearization is assigned to calculate the highlight positions, which depend on the
selection status and if there are constraints on a node. Then these snippets actually get
highlighted in the GUI linearization area (text and/or HTML).
At the end, the message from GF is appended.
100
10.4. Receiving the state
10.4.2. Probing
At several occasion, GF is asked in the background, whether a condition is fulfilled, or
to gather something. These GF calls have in common, that they do not take place in
the main class. The classes, which do that, are presented below:
For excluding self and result from the refinement menu, SelfResultProber, which is a
special class for this purpose, is used.
When building a reduced refinement menu below a coerce, RefinementMenuCollector
is used to get the list of the possible Subtype refinements, which are then refined and
RefinementMenuCollector is used again to get the individual property refinements. To
collect the properties of self, it is also used.
In the beginning, the printnames are loaded and cached (see section 9.1.4 for more about
that). This is done with PrintnameLoader, which might use TypesLoader if the user
wants the refinement menu entries to be annotated with their type.
The method detailed in section 9.3.7 for filling in Collection subtyping witnesses is implemented in SubtypingProber.
10.4.3. Undo handling
Since nearly all commands to GF change its state, the background probing classes have
the responsibility to clean up after them. GF accepts chain commands which are a list of
commands that are executed sequentially, where only the final state is sent back to the
editor. But sadly, these chain commands can not be undone as a whole, the individual
commands have to be undone. Hence, the prober classes have to issue the right right
amount of undo commands to GF.
To make the undo button in the GUI undo every user action, which might consist of
more than one GF command (access to a property of self or selecting a node where
a coerce is then automatically introduced), the editor has to keep track of number of
individual commands in each user action. This is done with a simple stack.
Sometimes, a user action leads to a state, where GF automatically executes another
command. In these cases, the top element of the undo stack is modified, so that a click
on the undo button undoes both commands. That way, each undo undoes a complete
user action, not just parts of it. Automatic commands hide something from the user for
a reason. Thus, they should not become noticeble through a back door like undo is one.
In the author’s eyes, counting undos should be the responsibility of the running GF.
There the actual command processing is done, so it should be easy to make chain commands atomic, so that 1 undo undoes a whole chain command. Then the undo stack
would still be necessar to undo automatically executed commands, though. But it would
be relieved from counting command constituents and their possible effects.
101
10. Implementation
10.5. Sending commands
The main work on the editor side to accept user actions has already been done.
If an entry in the refinement menu is selected, its command is sent to GF. For entries of
class InputCommand, where the most important data, the value that the user wants to
enter, is not yet known, a windows pops up and asks for it. Then it is given to GF.
When the user selects a tree node, a move command (mp) with the calculated position,
which is saved in the AstNodeData object attached to the tree node, is sent to GF.
After a click (or selection) in the linearization area, Linearization is given the start
and end indices (which in case of a simple click are equal). Linearization now goes
through the list of all registered linearization snippets and returns the according AST
position, which is then sent to GF. For a simple click, the position is the one stored for
the snippet the user has clicked on. It does not matter, in which language the user has
clicked, since all languages share the same AST.
In case of a selection, the node in the tree is selected, that has the smallest linearization,
that is at least partially covered by the selection. That means, the node closest to the
leafs, whose linearization is partially covered, is selected, since the linearization can only
grow, if nodes above are selected, since they consist of the combined linearization of
their children plus their own.
Note, that this mechanism has been devised by Janna Khegai for the old version of the
editor, but the implementation has been modified to a large extent in this work.
10.6. Integration into TogetherCC
The procedure, how the integration works, is described quite in detail in section 8.3,
so not much has to be added here. TogetherGFinterface is instantiated in a context
menu class of TogetherCC. Depending on whether it is the context menu of a method
or a class, the corresponding method in GFinterface is called. What happens there, is
described above.
102
10.6. Integration into TogetherCC
GF needs access to all
grammar files on a
place in the file
Manages parsing the old OCL
or the creation of the
constraint stubs, generating
Exports the UML model and
all referenced classes into a
format, that’s easily
system. This class
takes care of that.
the grammars and finally,
calling the editor.
parseable for the grammar
generator.
TempGrammarFiles
GFinterface
TogetherGFinterface
ModelExporter
Has access to the TogetherCC
specific classes, while
GFinterface only accesses an
abstraction of them.These
concrete fields are set in this
class.
Figure 10.6.: The classes, that interface the editor with TogetherCC
103
10. Implementation
104
11. Conclusions
11.1. User perspective
The goal in the beginning was a fuzzy one: ‘Make this editor (more) usable’. So part of
the work was refining this goal. This is mostly done in 7.
Many of the main blockers identified in section 7 have been removed. Only now the
editor is ready to be used in a real usability study to see, if the grammar based approach
for OCL really works out and makes editing them simpler, or if the price for not having
to know OCL, but having to cope with the grammar instead, is too high. This approach
with top-down editing is radically different from normal editors and especially novices
will have problems with it, as [KU93] states. There, it is recommended for that reason
to design for experienced users and not for novices, something which this work does not
follow. Whether that turns out to still be the right way, since the user is relieved from
the for him still unknown OCL syntax, remains to be studied.
11.2. Development
First, the author had to get an understanding about how exactly the old code worked.
As detailed in section 10.1, there were a number of impediments towards that.
The system consists of several parts and on all of them work needed to be done:
The program which laid the foundation for the rest was GF. But a number of additions in
GF were needed by the editor. These were chain commands (several commands in a row,
where the intermediate states are not sent to the editor), a way to solve GF constraints
without jumping to the root node and a command to print all available printnames. Also
a number of bugs were discovered during the development in this work and reported to
the GF developers.
The OCL standard types and operations are represented by GF grammars. These were
modified by the author to remove the generic subtyping witnesses for reflexivity and
transitivity to make it possible for GF to fill in this parameter of coerce without user
interaction1 . Also the comparison operators for Integer and the type-specific equality
1
Generic subtyping witnesses, where the concrete type depends on a type argument, are always applicable. Thus, reflexivity and transitivity were always offered. And if there is more than one possible
refinement, the user has to select. GF refines only if there wouldn’t be a choice anyway.
105
11. Conclusions
operators have been removed by the author. But the most important change was writing
the enhanced printnames for all OCL operations. Only with actual helpful content these
mechanisms were of any use.
These grammars only covered the standard OCL operations. The grammars for the
user’s UML model in contrast cannot be written in advance. Generating them is done
by the grammar generator by Kristofer Johanisson. Also this tool had to be changed to
produce better display names for classes and operations, tooltips (especially for marking
parameters for the automatic coercion) and to include the transitive hull of subtyping
witnesses.
If a previous OCL constraint is found, this is parsed by the OCL parser by Kristofer
Johanisson. Most of the changes here were not feature wishes, but corrected bugs or
fixed inconsistencies (something which also applies for the grammar generator).
The different parts are connected by a TogetherCC plug-in. A previous version for the
old grammars already existed, but large parts had to be rewritten by the author.
But the main work has been done on the editor itself. The new features of GF had to be
used here, enhanced printname support, HTML display was added, coercion handling
and nearly all of the other improvements were implemented in the editor.
11.3. Contributions
The main effect on Kristofer Johanisson’s work was giving it a nice GUI. For GF it
meant an overhaul of the editor with many improvements compared with the old one.
Thus, the new editor version was included as the official editor of GF 2.3. But the main
impact is on KeY, where the grammar based way to create OCL constraints got a nicer
face.
11.4. Only a specialised OCL editor?
The goal was to tailor the generic GF editor into a special one for OCL. But a number
of problems were of a more general nature and not specific to OCL. These are the
information display problems:
ˆ What does a specific fun do? – see section 7.1
ˆ A too long list of refinements – see section 7.2
ˆ What is the role of the current position in the AST? – see section 7.3
ˆ Output is just text without formatting – see section 7.4
106
11.5. In the view of EN ISO 9241 - 10
Devising a mechanism to solve these problems in general would also solve the problem
for OCL. What would be left would be writing the text, that actually explains the OCL
funs, groups them and describes their parameters. Regarding HTML display, nothing
had to be done on the grammar side there, since David Burke already did that in [BJ05],
which the author later on transferred to the German grammars, along with Burke’s other
improvements.
Some other problems also were not OCL specific:
ˆ The collapsing tree – see section 7.12
ˆ How to enter strings and integers? – see section 7.6
ˆ The safety question when exiting – see section 9.5.2
The tree problem has not been fixed in this work, but entering string and integer literals
is now made easy since a special entry shows up in the refinement menu and, if selected,
opens an input box without distracting choices to be made.
Also some enhancements were done that didn’t help OCL editing like enhanced middleclick parsing. These are mentioned in section 9.5.
And, not to forget, the improvements made to the source code of the editor mentioned
in section 10.1.
This list of generic enhancements made it possible to make the version of the Java GUI
enhanced in this work the new official GF editor. So this editor has been released as a
part of GF 2.3.
11.5. In the view of EN ISO 9241 - 10
The norm EN ISO 9241 covers ‘Ergonomic requirements for office work with visual
display terminals’. Part 10 of it contains a number of usability principles. The goal of
this section is to show, to which extent the editor fulfills these principles and how that
has been changed in this work.
Suitability for the task It is possible to create OCL constraints with the editor without
getting distracted by other things. GF grammars already make a pre-selection of what
kind of texts are creatable with them. Therefore, the user is restricted to what the
grammar he loaded offers, only the funs defined in this grammar and applicable at the
current position are offered. Others are not shown at all. Thus, the user can only edit
OCL constraints in the editor, but is not distracted with other options. But how well
users can cope with the grammar-based approach is still to be seen.
A number of editing steps of the user have been automated in this work. Coercions
are (not completely, though) done by the editor and properties of self are made more
107
11. Conclusions
accessible. The restructuring of the refinement menu made it easier to choose a wanted
fun. Hence, the user now got more support in doing what he wants to express. With
that and more descriptions, editing OCL became easier.
Self-descriptiveness Whenever a command to GF changes the editing state, the editor
will present the state to the user. So for most commands, the user will get feedback,
that reflects, what has been done. But for some commands like copying to the clipboard,
nothing is shown since the editing state has not been changed. Here, appending some
text to the linearisation area could help.
But still, for this principle, the biggest strides forwards have been made. All the OCL
funs got a description of what they express, the user gets an explanation of the current
node, the output is better readable. Before, in contrast, the user was out to guess, what
funs do, because there the often cryptic name was the only description available.
Controllability The user is forced to edit top-down. Beginning with the leafs and then
connecting them with suiting funs is only possible with wrapping, but type changes are
not allowed. Thus, the user is not free and cannot choose where to start. But he still
can select any already existing node in the linearization or tree and refine it and he can
go back and forth between them. So he is given a somewhat reduced freedom. Also
the user cannot apply any fun everywhere if it doesn’t fit; besides with changing type
arguments, the user is not allowed to make errors. But in return for these restrictions,
he gets correct OCL. So the lack of control has advantages, too.
Conformity with user expectations Grammar-based editing is different and the user
will probably not be accustomed to this kind of work-flow. So the editor breaks this
principle by design. But showing the user, what the current node does, is a step to help
him still finding his way.
Another thing is that all refining actions (even deleting or copy&paste) are offered in
the refinement menu. Even reading in input from the user can now be activated here
and is not hidden behind other buttons which are not helpfully labelled. It has been
moved there to match this principle. Only for selecting other nodes and for undo the
user has to search somewhere else.
Error tolerance The goal of the grammar-based approach is to make it impossible to
produce invalid OCL. So in theory, error tolerance shouldn’t be a problem.
But things are not that ideal, there are hidden type elements. When the user changes one
of them afterwards, type errors can be produced. But GF will report those constraints
and the editor will show all coerce nodes in the AST to give the user the possibility to
correct this erroneous state.
A different way to introduce typing errors is via coercions. Before, GF offered all funs
108
11.6. Future work
returning Instance ? at the Instance node of coerce, including funs with types that are
no subtype of the originally expected type. If such a fun was chosen, GF couldn’t fill in
the subtyping witness, but did not mark that as a possible error.
Now, in contrast, this situation normally is prevented from being possible. The refinement menu will only contain funs with a suiting subtype. Or with a type depending on
an argument, which can be a problem. When such a fun is refined and a ‘wrong’ type
selected, the according coerce node will be shown and the subtyping witness node will
be coloured red to make this error situation visible. Before, no hint of that there is an
error was given.
Also, exploring the editor with trial and error is possible, since unwanted actions can be
undone with GF’s undo mechanism.
Suitability for individualisation The different automatisms introduced in this work
or the display of linearisation languages can be switched off. Some minor things like
appending the type of the funs in the refinement menu to them can be switched on. But
more individualisation is not supported.
Different sets of fun descriptions as mentioned in section 9.1.4 would be a way to help
here. Different linearisation languages could get written and would instantly be supported by the editor, but the editor itself is not internationlised. This is still to be
done.
Suitability for learning OCL is displayed by default. All changes to the editing state
are also reflected in OCL. The user can start without knowledge of OCL, but gradually
see, which chosen action in the refinement menu along with its description belongs to
which output changes in OCL. This effect could even be enhanced by giving the OCL
syntax in the tooltip for a fun, but this has not been done yet. To implement that, only
the grammar files would have to be changed, so that shouldn’t be a big deal to support
that.
Watching the changes in OCL was possible before, but without descriptions and a sometimes not helpful linearisation, it was not really clear, what happened, and thus, learning
was impeded.
11.6. Future work
There is a number of things that this Diplomarbeit did not address, but still fall under
its topic. The collapsing tree and AtomSent have already been mentioned. But there
are more.
The jumping cursor GF still has a strange algorithm to compute, which node should
be selected next. In the editor, as it is designed now, this decision is completely left to
109
11. Conclusions
GF and should be fixed there. The tree is not analysed with respect to that. But since
the main GF developer recognised the jumping cursor of GF as a bug, it will be fixed
there, and the editor does not have to be adapted, to benefit from that.
Pseudo UML model features OCL features let definitions to introduce local shorthands for more complicated constructions. These are supported by the grammars, but
their use is quite complicated and has not been simplified by the editor. Offering an
entry in the refinement menu like ‘make to local let definition’, which automatically
introduces a suiting let definition and replaces the current subtree with this shorthand
would make life with let much easier for the user. Transforming bound variables in this
subtree to iterator variables which need a special fun to introduce them as parameters
would be the challenge here.
But let definitions are already an advanced part of OCL and the main point of this
work was to remove usability blockers, so in the eyes of the author it was justified to
postpone the support for let definitions to a later stage.
<<definition>> constraints are global shorthands which can be used in several con-
straints for the same context. That is something not applicable to the system as it is,
where only one constraint of one type (either method contract or invariant) is allowed
per context. Also, these semantics of these pseudo model features have been changed in
the past, and will be again, as they won’t be part of UML 2.0 (see [CK04]). One should
define these features in the model and add a special stereotype to them there. And that
way, funs will be generated for them as for normal features, so no additional work will
have to be done to support them. Therefore no steps have been made to support the
OCL 1.5 def construct.
Lists When the OCL parser sees three and conjoined boolean expressions in OCL, it
will use a list construct for that in the AST. These lists have the advantage that they
can be rendered in NL as bulleted lists, which shows their structure better compared
to producing just a long conjoined sentence. But to produce these lists in the editor is
a bit awkward and goes via AtomSent. Only when this work was nearing its end, GF
got a standardised way to formulate lists in it. It would be really helpful for the user to
make list construction easier, to abstract away from the nil and cons list constructors
and just show one list node in the AST, which has one child node for every list element,
and perhaps a node labelled ‘...’, that produces a new list element when clicked.
Drag & Drop To move a node in the AST to another position at the moment is only
possible by using the clipboard of GF and has to be done manually2 . Furthermore,
moving is restricted by bound variables. If used in the subtree of the node which is to
be moved, they also have to be available at the target position. GF will check for that
and only offer the refine from clipboard command, if they match.
2
Actually, short before the deadline of this work a special command has been implemented in GF,
that would make drag&drop easier to implement.
110
11.6. Future work
But why not automate the use of the clipboard and give the user the possibility to use
drag & drop? If a drag starts, copy the node. When hovering over a possible target
node, ask GF in the background, whether the dragged node is pasteable there or not.
If it is, allow a drop, if not, change the cursor accordingly to signal that. This could
even be supported in the linearisation area. Select a body of text there and drop it over
a question mark, either as a copy or as a move. That would make reordering the tree
much simpler.
Custom linearisation rules For English, the heuristics that transform a class name
into a common noun work quite well, for German they don’t. This problem was there
already at the beginning ([Dan03]), but has not been fixed yet. A solution would be to
add additional JavaDoc tags to the classes and methods that contain GF lins. These
could then be used by the grammar creator.
The problem with this approach is, that this cannot be done automatically, and would
require some familiarity with the GF syntax, although common nouns are are not that
hard to write. Also, GF 2.3 offers linearisation rules by example, which could take away
the need to know the GF syntax at all, so that would be worth trying out.
Renaming bound variables When the linearisation of a bound variable is clicked, the
first node of the subtree, which is the scope for this variable, is selected. But that
is not the node which introduces the variable, as it is usual for higher order abstract
syntax variable treatment. GF does not recognise bound variables as separate entities,
so they cannot be selected in the AST as such. And selecting would be the first step for
renaming.
Now the user has to use the button alpha and enter both the old and the new name.
Not even a list to select the variable is offered, although that could be done without
support from GF.
Saving in between As mentioned above, OCL cannot be saved as a JavaDoc comment
in the middle of an editing session. To make the save and load buttons save in the model,
and to offer an additional menu entry Save As, which would do, what Save does now
(saving in an extra file), would be something expected by a user. The same goes for
loading. Also being able to revert to the state directly after loading would be nice.
Parsing Users who have been shown the editor, expressed the wish to enter OCL by
hand and have it parsed. Using the editor is different from the free-form text approach
of normal programming. And the point of the editor is to make entering OCL by hand
unnecessary. But nevertheless it would be nice to support parsing, since the editor may
slow down more experienced users for simple expressions for which they already know
the syntax.
111
11. Conclusions
The GF built-in parser is, as mentioned before, limited and cannot be used to parse
OCL because only a small, not yet known fragment is parsable. There is the external
parser, but that only knows how to parse complete OCL documents, not only parts.
And the OCL has to be complete, no open metavariables may be left. And that usually
is not the case. So the parser would have to be changed to allow that.
Architecture As mentioned in section 10.1.5, the system does not yet conform to one
of the more advanced separation patterns of GUI and model. So continued refactoring
efforts have to be made to separate the GUI, the editing state and the GF calls.
Speed The most annoying thing about the editor at the moment is the loading time.
50 seconds are just too long to be used for small constraints for single methods/classes
on a Pentium M with 1.5 GHz and 512 MB RAM (depending on the memory load, this
figure can vary between 40 and 75 seconds). And for each method/class, the editor must
be loaded again. Only the previously generated grammar files can be used again. Here,
much has to be done to make the editor acceptable.
Reusing the loaded state and just giving GF a new OCL context would be one way to
make consecutively editing constraints easier. But as soon as the model changes, the
grammars also have to be changed, which would make a long reload necessary. Another
problem is the memory consumption. The GF process takes 179 MB by itself, which
with just 512 MB is noticable and produces quite some swapping.
Loading time and memory consumption could be reduced by making the loaded languages selectable, especially deselecting German with its huge resource grammars would
really help here. Loading only English and OCL as concrete languages reduces the initial
loading time from 50 to 15 seconds and GF’s memory consumption goes down to 60 MB.
Another performance problem is how the HTML indices are calculated. Snippets are
not appended to the JTextPane, but appended to a StringBuffer, which is then given
as a new HTML document to the JTextPane, so that for every following snippet, the
previous ones have to be parsed again. And this is a quadratic algorithm and lengthier
constraints are noticeable slower than short ones. A HTML parser where parts can be
added to the already parsed HTML without a need for reparsing the whole document
could remedy that.
Also here, switching off one language helps since it reduces the amount of GF snippets
by around one third.
What also slows the editor down are the GF calls in the background. When they are
read, only some parts of the GF state are parsed, which always excludes the linearisation.
But still, calling GF takes time.
But as long as the editor wants to know something about possible refinements at a
certain position or if a command can be filled in automatically, it has to ask GF. This
intelligence doesn’t come for free.
In the original design, as stated in section 8.1, the editor was meant to have no knowledge
112
11.6. Future work
about the types in the tree, it should just display the AST. All the intelligent work was
supposed to be done by GF with the editor being just a dumb GUI for it.
In this work, this division of labour has been broken up to some extent. The editor
now does some domain specific analysis of the editing state and changes it accordingly
(namely automatic refinements). It is a layer above GF, which uses GF, but some
functionality of it is closely tied to what GF already does. Removing self and result
from the refinement menu is such an example. Here the editor just looks one step ahead
in the refinement menu, if a refinement can have all its children filled in or not in a
given situation. This checking is not of a higher level than what GF does. It is just not
advisable for the generic GF to always look farther into the future than necessary, since
that could be quite expensive and is only needed for two funs of the OCL grammars.
So there are reasons to put these computations into the specialised editor and not into
GF. But that incurs the run-time cost of calls to the external GF process.
Optimisation has been postponed in this work to future works. First see, what can be
done with the editor, what can it do for the user, and only after that, see, how it can be
done faster.
Other formal languages The improvements, which were implemented for the editor in
this work, were done with OCL in mind. But, as described in section 11.4, not everything
done was OCL specific. Especially for formal languages, most of the improvements beside
the mentioned generic ones can be reused.
For all models which feature subtypes, this has to be emulated with coercions. And
this problem is more general and not specific to OCL. Even the code for that is generic
enough to be applicable even to another specification language like JML3 , which recently
got supported by KeY. Only the category names for classes and instances and the fun
coerce itself have to be the same.
The same ways to prohibit a type-incorrect use of OCL’s self and result can be used for
JML’s this and \result, namely the guard argument and the editor which probes if
this respectively this can be refined in the current situation. If the funs are called the
same, the code should work out of the box. The same holds for the properties of this
and how they can be made more accessible.
And if someone wrote a concrete grammar for a subset of JML for the abstract OCL
grammar, no changes would be necessary in the editor at all. Just that writing such a
grammar would be quite complicated.
According to [Ham04], for the part of JML, that is also covered by OCL, a concrete
grammar theoretically should be writeable, although quite a number of helper constructions would be needed, that would blow up the linearisation in JML. Some steps in
the reverse direction have already been taken, like introducing a null value and class
or a statement excThrown, but already arrays make things complicated (see [Rot02] for
details) and would also need helper definitions (they are silently treated as Sequences in
3
http://www.cs.iastate.edu/~leavens/JML/
113
11. Conclusions
the current GF OCL grammars).
And specifying loops, which are part of the implementation and not of the model, is
not possible in OCL at all. OCL is restricted to the model and no ‘access’ to the
implementation is possible. Hence, these parts cannot be translated.
11.7. Related work
The tool that somehow goes a step in the same direction is Octopus4 . It features a
display of OCL constraints in a language called Business Modelling Language, which
resembles SQL. That way, SQL developers have a easier time at understanding, what an
OCL constraint does. But only OCL can be translated into BML, the reverse direction
is not supported. Thus, no changes can be done there, it is read-only. But the main
focus is model-driven prototyping and code-generation.
The tool olce5 also goes into this direction, together with execution and debugging.
Modelling and simulation is the goal of Xactium6 . In both there is support for editing
OCL. They feature a text editor combined with code-completion or navigation-help,
together with checking if the OCL is well-formed.
But for editing OCL an approach is used in this work that has not been covered before.
4
http://www.klasse.nl/english/research/octopus-intro.html
http://lci.cs.ubbcluj.ro/ocle/index.htm
6
http://albini.xactium.com/content/index.php?option=content&task=section&id=
7&Itemid=49
5
114
A. Format of the Printnames
The following is the documentation about the syntax used for the printnames. For GF,
it is also available as an individual file.
A.1. Introduction
The main new feature of the new version of the graphical syntax editor for GF, called
gfeditor, are enhanced printnames. Printnames are a feature of GF available for concrete
grammars that are displayed in the editor instead of the name of the fun in the abstract
grammar. Short fun names might be enough for a grammarian, but for the unacquainted
user, they are not. In figure A.1 you can see an example comparison for the OCL
grammars.
Printnames in GF are just strings, that contain no special code. They used to be
displayed as they are written in the grammars.
Now some processing is done. The following is possible with them:
Tooltips Possible refinements can get tooltips attached to them to give the user of the
editor more hints about what will happen (see figure A.2)
Grouping Refinements can now be grouped to make large lists more concise (see figure
A.2)
Parameter descriptions These are displayed at several locations:
ˆ Below the fun description in the tooltip for the entries in the refinement
menu, as also visible in figure A.2
ˆ As tooltips in the AST for unrefined nodes, as visible in figure A.3
ˆ Above the refinement menu as the current goal, to give the user a hint about
what is expected from him now (also figure A.3)
115
A. Format of the Printnames
(a) without printnames
(b) with printnames
Figure A.1.: The refinement menu of the OCL grammars. On the left without printnames, with only the abstract fun names, on the right with printnames.
A.2. How to use the enhanced tooltips
The following is an example of where all printname features are used:
p r i n t n a m e excludes = [ " element is not included in collection " ]
++ [ " \\ $ False iff the given object is an element of the given
collection . " ]
++ [ " \\% COLL { Collection operations " ]
++ [ " \\ $ Operations on collections predefined in OCL . " ]
++ [ " <br > These operate on the OCL collection types , " ]
++ [ " not on Vectors , AbstractLists of the implementation
language .} " ]
++ [ " \\# collElemType The official element type of <i > coll </ i >. "
]
++ [ " <br > That is , the parameter type of <i > coll </ i > " ]
++ [ " \\# instType The type of <i > elem </ i >. " ]
116
A.2. How to use the enhanced tooltips
Figure A.2.: Grouping, tooltips and parameter descriptions
Figure A.3.: Parameter description as tooltip in the abstract syntax tree
117
A. Format of the Printnames
++ [ " \\# elem The instance that must not be an element of <i >
coll </ i >. " ]
++ [ " \\# coll The Collection which must not include an element " ]
++ [ " <br > This Collection must be parametrized with (
collElemType ) " ];
Processed markup commands are \\$ , \\% and \\# .
A.2.1. Tooltips
\\$ starts a tooltip and ends the displayed text. If you have the following printname:
p r i n t n a m e excludes = [ " element is not included in collection " ]
++ [ " \\ $ False iff the given object is an element of the given
collection . " ]
then ‘element is not included in collection’ will appear in the refinement menu, and
‘False iff the given object is an element of the given collection.’ as the tooltip. Tooltips
are always displayed as HTML, so this markup language can be used here. Note, that
for the text displayed in the refinement list, no HTML can be used.
A.2.2. Grouping
With \\% it is possible to define groups of refinements. Everything from \\% to the next
space or { is taken as a tag for a subcategory group. All refinements having the same tag
will be grouped under one entry in the left refinement menu. In figure A.2 for example,
you can see all entries, that have the tag \\%COLL in the right menu. A tag is defined by
use, it does not have to be declared in advance. The tag is independant from the types
of the funs of GF, so funs with arbitrary types can be in the same group. They just
won’t normally appear together at the same time, since GF only permits type-correct
refinements, and normally only one type is correct.
In figure A.4 you can see, that subcategories can have more elaborate descriptions than
just \\%COLL. If a subcategory tag is directly followed by a {, everything until the next
} is taken as the display text for that subcategory. As for display texts for funs, \\$ is
the delimiter between the text, that will be displayed directly in the refinement menu,
and what is displayed as the tooltip. As for fun tooltips, HTML can be used here.
If only one member of a group is available, the group won’t be displayed. The refinement
will be listed instead in the left menu as a top category item.
Note that it is not possible to use a group hierarchy. Only one level of grouping is
possible.
118
A.2. How to use the enhanced tooltips
Figure A.4.: Using a nicer display texts and tooltips for subategories in the refinement
menu
A.2.3. Parameter descriptions
Each parameter of a fun can get a description in the printname of this fun. The places
where this information is used, are listed at the end of section 6.
Giving the parameters descriptions works a bit like in JavaDoc. They look like the
following:
[ " \\# collElemType The official element type of <i > coll </ i >. " ]
First comes a special tag, \\#. This is directly, without a space, followed by the name of
the parameter1 . The name is ended by the first space. After that everything until the
next \\# is part of the description of that parameter. HTML can be used here.
The order of these parameter descriptions is important, because they are not checked
against the signature of their fun. The first description will be used for the first parameter and so on. If there are more descriptions than parameters, these superfluous
descriptions get read, but are never used. If there are too less, then only the present parameters get described, while the rest gets no tooltips and the generic ‘choose action on
subterm’ will be displayed above the refinement menu, if such a parameter is selected.
1
There is one exception to this. A ’ !’ is allowed here. This is used in OCL mode and triggers the
automatic insertion of a coerce. For general grammars, the ’ !’ is simply ignored.
119
A. Format of the Printnames
120
Bibliography
[ABB+ 05] Wolfgang Ahrendt, Thomas Baar, Bernhard Beckert, Richard Bubel, Martin
Giese, Reiner Hähnle, Wolfram Menzel, Wojciech Mostowski, Andreas Roth,
Steffen Schlager, and Peter H. Schmitt. The KeY tool. Software and System
Modeling, 4:32–54, 2005.
[BH03]
Richard Bubel and Reiner Hähnle. Formal specification of security-critical
railway software with the KeY system. In Thomas Arts and Wan Fokkink, editors, Proc. Eighth International Workshop on Formal Methods for Industrial
Critical Systems (FMICS 03), volume 80 of Electronic Notes in Theoretical
Computer Science. Elsevier, 2003.
[BJ05]
David A. Burke and Kristofer Johannisson. Translating formal software
specifications to natural language—a grammar-based approach. In Philippe
Blache, Edward Stabler, Joan Busquets, and Richard Moot, editors, LACL
2005, number 3492 in LNAI, pages 51–66. Springer, 2005.
[Bub02]
Richard Bubel. Formale Spezifikation und Verifikation sicherheitskritischer
Software mit dem KeY-System. Master’s thesis, Universität Karlsruhe, 2002.
[CK04]
Maria Victoria Cengarle and Alexander Knapp. Ocl1.4/5 vs. 2.0 expressions – formal semantics and expressiveness. Software and Systems Modeling, 3(1):9–30, March 2004. http://www4.in.tum.de/lehre/seminare/hs/
WS0405/uml/CK04b.pdf.
[Dan03]
Hans-Joachim Daniels. Eine Deutsche Grammatik für OCL, Studienarbeit,
2003. http://www.cs.chalmers.se/~krijo/gfspec/daniels03.pdf.
[Ham04]
Ali Hamie. Translating the Object Constraint Language into JML. In
19th ACM Symposium on Applied Computing, pages 1531 – 1535, Nicosia,
Cyprus, 2004. http://cmis.mis.brighton.ac.uk/Research/vmg/papers/
SAC2004.pdf.
[HJR02]
Reiner Hähnle, Kristofer Johannisson, and Aarne Ranta. An authoring tool
for informal and formal requirements specifications. In Ralf-Detlef Kutsche
and Herbert Weber, editors, Fundamental Approaches to Software Engineering (FASE), Part of Joint European Conferences on Theory and Practice of
Software, ETAPS, Grenoble, volume 2306 of lncs, pages 233–248. spv, 2002.
121
Bibliography
[Joh04]
Kristofer Johanisson. Disambiguating implicit constructions in OCL. In
OCL and Model Driven Engineering Workshop at the UML Conference in
Lisbon, 2004. http://www.cs.kent.ac.uk/projects/ocl/oclmdewsuml04/
description.htm.
[Joh05]
Kristofer Johanisson. Formal and Informal Software Specifications. PhD
thesis, Göteborg University, Chalmers University of Technology, June 2005.
http://www.cs.chalmers.se/~krijo/thesis/thesisA4.pdf.
[Johar]
Kristofer Johanisson.
The KeY Book, edited by Bernhard Beckert,
Rainer Hähnle and Peter Schmitt, chapter Natural Language Specifications.
Springer, LNAI, to appear.
[Khe03]
Janna Khegai. Java GUI syntax editor for GF 1.1. Internet, March
2003.
http://www.cs.chalmers.se/~aarne/GF/doc/javaGUImanual/
javaGUImanual.htm.
[KU93]
Amir Ali Khawaja and Joseph E. Urban. Syntax-directed editing environments: Issues and features. Technical report, Arizona State University, 1993.
[Mar02]
Robert C. Martin. The Principles, Patterns, and Practices of Agile Software
Development, chapter The Single Responsibility Principle. Prentice Hall,
2002. http://www.objectmentor.com/resources/articles/srp.
[McB00]
Conor McBride.
Dependently Typed Functional Programs and their
Proofs.
PhD thesis, LFCS, University of Edinburgh, Edinburgh,
Scotland, 2000. http://www.lfcs.informatics.ed.ac.uk/reports/00/
ECS-LFCS-00-419/index.html.
[Ran04]
Aarne Ranta. Grammatical framework: A type-theoretical grammar formalism. The Journal of Functional Programming, 14(2):145–189, 2004.
[Rot02]
Andreas Roth. Deduktiver Softwareentwurf am Beispiel des Java Collections
Frameworks. Diplomarbeit, Fakultät für Informatik, Universität Karlsruhe,
June 2002.
122

Similar documents