56 KAPITEL 5: IMPLEMENTIERUNG– PowersetIteratorIteriert über die Potenzmenge einer spezifizierten Kollektion. Für die Kollektion(A,B,C)würde ein PowersetIterator beispielsweise folgende Mengen in Form von java.util.LinkedList-Instanzen liefern:({}, {A}, {B}, {A,B}, {C}, {A,C}, {B,C}, {A,B,C}).Die Parameterkollektion wird, wie auch bei anderen Iteratoren aus diesem Package, überihren Iterator spezifiziert.Ein ausführlicher Kommentar zu den Klassen im Package quest.util.collections befindetsich im Quellcode <strong>und</strong> in [<strong>ODL</strong>API]. Ein Klassendiagramm des Packages wird im AnhangA auf der Abbildung A.7 gezeigt.Da die oben beschriebenen Klassen im Zuge der Implementierung von MetaType-Unterklassenfür die neuen <strong>ODL</strong>-Datentypen erstellt wurden, wollen wir angeben, welche Datentypenvon welchen Iteratoren Gebrauch machen, um in ihrer instances-Methode einen Iteratorüber die Werte des jeweiligen Typs zu konstruieren:<strong>ODL</strong>-Datentyp Klasse Benutzte Klassen ausquest.util.collectionsProdukttyp MetaProductType CompositeIterator,ResettableIteratorEingeschränkter Typ MetaRestrictedType ConditionedIteratorConditionedIterator.ConditionResettableIteratorMengentyp MetaSetType PowersetIteratorResettableIteratorAn dieser Stelle ist ein Hinweis zur Gestaltung effizienter <strong>ODL</strong>-Abfragen notwendig: die (indirekte)Benutzung der Klasse CachedIterator bei der Iteration durch Tupel eines Produkttyps(indem MetaProductType auf die Klasse CompositeIterator zurückgreift, dieihrerseits CachedIterator benutzt) hat den Vorteil zur Folge, dass eine Abfrage der Formexists var:( p1:Port, p2:Port, c:Component ).(var.p1.Component = var.c and var.p2.Component = var.c )durch das Caching zum Teil erheblich schneller ausgewertet wird, als die Abfrageexists p1:Port. exists p2:Port. exists c:Component.(p1.Component = c and p2.Component = c ).Dies sollte beim Entwurf <strong>und</strong> Optimierung von <strong>ODL</strong>-Abfragen berücksichtigt werden. MehrInformationen zum Entwurf effizienter Abfragen gibt es im Abschnitt 5.4.• Benamte PrädikateFür die benamten Prädikate waren zwei Anwendungsfälle zu implementieren:1) Deklaration eines benamten Prädikats. Beispiel:compHasSubComps( comp:Component ) :=size( comp.SubComponents ) > 02) Aufruf eines zuvor deklarierten benamten Prädikats. Beispiel:exists c:Component. call compHasSubComps( c )Wir beschreiben nun die Implementierung beider Anwendungsfälle.
5.1 ERWEITERUNG DES SPRACHUMFANGS 57– Deklaration eines benamten PrädikatsBetrachten wir zunächst die Deklaration eines benamten Prädikats. Die semantische Analyse<strong>und</strong> die Kompilation des <strong>ODL</strong>-Terms, der den Rumpf eines benamten Prädikats bildet,unterscheidet sich nicht von der Verarbeitung eines <strong>ODL</strong>-Terms in einer üblichen <strong>ODL</strong>-Abfrage. Der Unterschied tritt erst bei der Behandlung des kompilierten <strong>ODL</strong>-Terms zuTage – er wird vom SableCCGenerator nicht als Ergebnis der Kompilation an dasaufrufende Objekt (typischerweise eine Instanz von quest.odl.evaluation.EvaluationModelGenerator)zurückgegeben, sondern in einer Instanz der neuen KlasseNamedPredicate gekapselt <strong>und</strong> in einer internen Hashtabelle unter dem in der Prädikatsdeklarationangegebenen Namen für spätere Aufrufe gespeichert. Für diese Sonderbehandlungsind im SableCCGenerator die Methoden outANamedPredicateOdl-Start, outANamedPredicateDeclaration <strong>und</strong> caseANamedPredicateDeclarationzuständig. Eine NamedPredicate-Instanz enthält alle für den Aufruf einesbenamten Prädikats notwendigen Angaben – die Parameterliste des benamten Prädikats <strong>und</strong>seinen Rumpf, der durch einen <strong>ODL</strong>-Term repräsentiert wird.Diese Verarbeitungsweise hat den positiven Nebeneffekt, dass benamte Prädikate, die im<strong>ODL</strong>-Editor während einer QUEST-Sitzung deklariert wurden, bis zum Ende dieser Sitzungverfügbar bleiben, ohne erneut eingegeben werden zu müssen.– Aufruf eines benamten PrädikatsWir beschreiben nun die Implementierung des Aufrufs eines benamten Prädikats. Dafürwird die Klasse NamedPredicateTerm eingesetzt: sie erfüllt die Rolle eines Adapters(s. auch [GammaEtAl], S.171-185), der einer NamedPredicate-Instanz die Schnittstelleeines Terms gibt. Diese Trennung zwischen NamedPredicate <strong>und</strong> NamedPredicateTermwird gemacht, um die Auswertung eines benamten Prädikats von der Berechnungseiner Parameter abzukoppeln, die ja beliebige <strong>ODL</strong>-Ausdrücke sein dürfen. Die Auswertungder Parameter wird von NamedPredicateTerm vorgenommen <strong>und</strong> die Ergebnissewerden als Parameterwerte an den NamedPredicate weitergegeben.Im SableCCGenerator sind die Methoden outANamedPredicateUnaryProposition,outANamedPredicateCclUnaryProposition <strong>und</strong> outANamedPredicateCallfür die Erstellung des Aufrufs eines benamten Prädikats zuständig. Die erstenbeiden dienen lediglich dazu, einen bereits erstellten NamedPredicateTerm weiterzugeben.Die Erstellung <strong>und</strong> Überprüfung eines NamedPredicateTerm findet in derMethode outANamedPredicateCall statt. Hier wird zunächst überprüft, ob das aufgerufenePrädikat bereits deklariert wurde, dann wird überprüft, ob die Anzahl <strong>und</strong> dieTypen der Parameter mit der deklarierten Signatur des benamten Prädikats übereinstimmen.Schließlich wird ein NamedPredicateTerm erstellt, der das aufzurufende NamedPredicatekapselt.• CCL-PropositionenFür die CCL-Propositionen existiert in der <strong>ODL</strong>-Grammatik ein eigener Ableitungszweig, dermit einigen Ausnahmen mit dem für <strong>ODL</strong>-Propositionen übereinstimmt (s. Abschnitt 5.1). Dadie Verarbeitung von Syntaxbaum-Knoten für eine CCL-Proposition <strong>und</strong> eine <strong>ODL</strong>-Propositiongleich ist, wurden im SableCCGenerator die Methoden für CCL-Propositionen erstellt,indem entsprechende Methoden für <strong>ODL</strong>-Propositionen kopiert <strong>und</strong> die Namen der Methoden<strong>und</strong> der in Methodenrümpfen aufgerufenen Methoden angepasst wurden. Beispielsweise wurdeaus der Methode für die AND-Verknüpfung von zwei <strong>ODL</strong>-Propositionen:public void outAAndProposition(AAndProposition node) {Term leftTerm = (Term)getOut( node.getProposition() );Term rightTerm = (Term)getOut( node.getUnaryProposition() );setOut( node, new Conjunction(leftTerm, rightTerm) );}