An Examination of DSLs for Concisely Representing
Model Traversals and TransformationsJeff Gray
University of Alabama at Birmingham
Gábor Karsai Vanderbilt University/ISIS
HICSS-36
This work is supported by the DARPA IXO, under the Program Composition forEmbedded Systems (PCES) program, Contract Number: F33615-00-C-1695
Support for this project was also provided by Boeing.
Overview
Three DSLs in two different applications Applications: Tool integration and aspect-oriented
domain modeling (AODM) Focus of DSLs:
Generation of complex data structures from high-level specifications
Synthesis of iterative processes Generation of API wrappers from high-level specs
Quantitative description of generated code
Integrated Model Server (IMS)
AEFRFMECA
ADVISE
GME
RELEX
BrowserCOM
CORBA
ADO
ADO
Text
Excel (csv)
IMS
Tool Integration Framework (TIF)
ORB
Integrated Model
Browser
Legacy Tool 1
Tool Adapter
Integrated Tool
Naming Service
Legacy Tool N
Tool Adapter
Common Model Interface
Meta-Model Objects
IMS
Semantic Translation
Integrated Model Database
…
IMS models can be viewed in a web browser
New tools can access the IMS directly through the CMI
Legacy tools require abi-directional tool adapter
The CMI is specified in CORBA IDL and defines rules and data structures
for accessing the IMS
MS Repository sits on top of an ODBC database; currently Access or SQL Server
CORBA
Tool Integration Framework (TIF)
ORB
Integrated Model
Browser
Legacy Tool 1
Tool Adapter
Integrated Tool
Naming Service
Legacy Tool N
Tool Adapter
Common Model Interface
Meta-Model Objects
IMS
Semantic Translation
Integrated Model Database
…
IMS models can be viewed in a web browser
New tools can access the IMS directly through the CMI
Legacy tools require abi-directional tool adapter
The CMI is specified in CORBA IDL and defines rules and data structures
for accessing the IMS
CORBA
Focus of this portion of the talk:
DSLs for representing tool models as stored in the CMI
DSLs for representing model translation as traversal/visitor specifications
Pieces of the Translation
Structure of the models: Objects to be traversed What are the possible paths for traversals?
Traversal sequences: How to traverse? What are the desired paths for traversals?
Visitors: Actions to be taken What to do?
Phases of processing Multiple passes over the structure
Entity_2
Top_Model
Entity_1 Component
1
*
Tool Specification (Structure)paradigm Foo;
model Top_Model { ... part Component components;}
model Component { ... part Entity_1 ent_1; part Entity_2 ent_2; part Component subComponents; rel Rel aRel}
entity Entity_1 { ... }
entity Entity_2 { ... }
relation Rel { Entity_1 src 1<->Entity_2 dst *;}
Generation of Data Structuresparadigm RELEX;
model RELEX_Model { attr string Name;
part RELEX_Object objects; part Failure_Mode failure_Modes; rel RELEX_Failure_Mode rel_FM;}
entity RELEX_Object { attr string Name; attr string Part_Number; attr string User_Text; attr string Reference; attr double Failure_Rate;}
entity Failure_Mode { attr string Failure_Mode; attr string Local_Effects; attr string Next_Effects; attr string End_Effects; attr string Failure_Detection_Method; attr long Mode_Severity_Code; attr double Failure_Mode_Ratio; attr double Failure_Effect_Probability; attr double Failure_Rate;}
relation RELEX_Failure_Mode { RELEX_Object Owner 1 <-> Failure_Mode Failures *;}
…
int RELEX_Model_M::get_objects(vector<RELEX_Object_E>& _res) { int count = 0; MetaEntity* type = MetaData::LookupEntity("RELEX","RELEX_Object"); vector<InstEntity*>::iterator itr; vector<InstEntity*>* list = obj->parts(); for(itr = list->begin(); itr != list->end(); itr++) { if((*itr)->type() == type) { _res.push_back(RELEX_Object_E(*itr)); count++; } } return count;}
…
Structured Specification of Translators(Traversal/Visitor)visitor Visitor
{
at Component[...]
<<...>> traverse[...];
at Entity_1[...]
<<...>>;
at Entity_2[...]
<<...>>;
at Rel[...]
traverse[...];
}
traversal Traversal using Visitor
{
from Top_Model ->[…]
<<...>> to { components[...] } <<...>>;
from Component[...]
to { entity_1[...], entity_2[...],
subComponents[...], rel[...]
};
from Rel[...]
<<...>> to {src[...], dst[...] } <<...>>;
}
Generation of Traversal/Visitors
… from Component[IMS::Component_M& parent] to { failureModes[parent,fMap], discrepancies[parent,dMap], monitors[parent,mMap], faultReports[parent], subComponents[parent,pcMap], fmMonitor[parent,fMap,mMap], fmDiscrepancy[parent,fMap,dMap]};…
…void Traversal_T::traverse(GME_4_0::Component_M& self, IMS::Component_M& parent) { vector<GME_4_0::FailureMode_E> _lst; self.get_failureModes(_lst); vector<GME_4_0::FailureMode_E>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::FailureMode_E arg=GME_4_0::FailureMode_E(*_itr); vis->visit(arg,parent,fMap); } vector<GME_4_0::Discrepancy_E> _lst; self.get_discrepancies(_lst); vector<GME_4_0::Discrepancy_E>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::Discrepancy_E arg=GME_4_0::Discrepancy_E(*_itr); vis->visit(arg,parent,dMap); } vector<GME_4_0::Monitor_E> _lst; self.get_monitors(_lst); vector<GME_4_0::Monitor_E>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::Monitor_E arg = GME_4_0::Monitor_E(*_itr); vis->visit(arg,parent,mMap); } vector<GME_4_0::Fault_Report_E> _lst; self.get_faultReports(_lst); vector<GME_4_0::Fault_Report_E>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::Fault_Report_E arg=GME_4_0::Fault_Report_E(*_itr); vis->visit(arg,parent); } vector<GME_4_0::Component_M> _lst; self.get_subComponents(_lst); vector<GME_4_0::Component_M>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::Component_M arg = GME_4_0::Component_M(*_itr); vis->visit(arg,parent,pcMap); } vector<GME_4_0::FMMonitor_R> _lst; self.get_fmMonitor(_lst); vector<GME_4_0::FMMonitor_R>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::FMMonitor_R arg = GME_4_0::FMMonitor_R(*_itr); vis->visit(arg,parent,fMap,mMap); } vector<GME_4_0::FMDiscrepancy_R> _lst; self.get_fmDiscrepancy(_lst); vector<GME_4_0::FMDiscrepancy_R>::iterator _itr; for(_itr = _lst.begin(); _itr != _lst.end(); _itr++) { GME_4_0::FMDiscrepancy_R arg=GME_4_0::FMDiscrepancy_R(*_itr); vis->visit(arg,parent,fMap,dMap); }}…
Achieved Goals - Tool Integration Using a DSL, the underlying CORBA data
structures and service calls that are needed to perform the model integration are hidden.
Using a DSL, the often tedious and repetitive code fragments that are needed for iteration and transformation can be more concisely/intuitively specified.
Separation of concerns: Cleaner solution by separating semantic
(translator) and syntactic (adapter) issues
Model-Integrated Computing (MIC) with the Generic Modeling Environment (GME)
• Generic Modeling Environment (GME) is a domain-specific modeling tool• Grew out of over 14 years of research on computer-based systems in aerospace, instrumentation, manufacturing and robotics.• It can be utilized in many different domains by providing a meta-level paradigm description. Paradigm describes all of the entities of the domain, as well as valid relationships• A modeler first loads the domain paradigm and then constructs new models in that domain
Available for download at http://www.isis.vanderbilt.edu
Please see November 2001 issue of IEEE Computer
Difficulties in Managing Constraints
A
B
c d e1 2
3
B
c d e1'' 2'' 4
F
B
c d e1' 2'
3'
4Multiple Levelsof Hierarchy Replicated
Structures
ContextSensitive
ChangeMaintenance???
Process of Using a Model Weaver
GME
Domain-Specific
Weaver
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE project SYSTEM "mga.dtd">
<project guid="{00000000-0000-0000-0000-000000000000}" cdate="Thu Nov 30 14:15:40 2000" mdate="Thu Nov 30 14:15:40 2000" metaguid="{00000000-0000-0000-0000-000000000000}" metaname="PCES"><name>bit1</name><comment></comment><author></author><folder id="id-006a-00000001" kind="RootFolder"><name>bit1</name><folder id="id-006a-00000002" kind="Structural"><name>Structural</name><model id="id-0065-00000001" kind="ProcessingCompound"><name>ProcessingCompound</name><attribute kind="Description" status="meta"><value></value></attribute><atom id="id-0066-00000007" kind="Attribute" role="Attrib"><name>GatesPerBit</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>37,153</value></regnode></regnode></regnode><attribute kind="Value" status="meta"><value></value></attribute></atom><atom id="id-0066-00000006" kind="Attribute" role="Attrib"><name>NomBits</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>205,76</value></regnode></regnode></regnode><attribute kind="Value" status="meta"><value></value></attribute></atom><atom id="id-0066-00000005" kind="Attribute" role="Attrib"><name>MaxBits</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>128,76</value></regnode></regnode></regnode>
Enhanced
FOO.XML
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE project SYSTEM "mga.dtd">
<project guid="{00000000-0000-0000-0000-000000000000}" cdate="Thu Nov 30 14:15:40 2000" mdate="Thu Nov 30 14:15:40 2000" metaguid="{00000000-0000-0000-0000-000000000000}" metaname="PCES"><name>bit1</name><comment></comment><author></author><folder id="id-006a-00000001" kind="RootFolder"><name>bit1</name><folder id="id-006a-00000002" kind="Structural"><name>Structural</name><model id="id-0065-00000001" kind="ProcessingCompound"><name>ProcessingCompound</name><attribute kind="Description" status="meta"><value></value></attribute><atom id="id-0066-00000007" kind="Attribute" role="Attrib"><name>GatesPerBit</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>37,153</value></regnode></regnode></regnode><attribute kind="Value" status="meta"><value></value></attribute></atom><atom id="id-0066-00000006" kind="Attribute" role="Attrib"><name>NomBits</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>205,76</value></regnode></regnode></regnode><attribute kind="Value" status="meta"><value></value></attribute></atom><atom id="id-0066-00000005" kind="Attribute" role="Attrib"><name>MaxBits</name><regnode name="PartRegs"><value></value><regnode name="StructuralAspect"><value></value><regnode name="Position" isopaque="yes"><value>128,76</value></regnode></regnode></regnode>
FOO.XML
constraint FOOB2
{
// apply a specific constraint to “B2” only
in Structural models("ProcessingCompound")->
select(p | p.name() == "B2")->PowerStrategy(1, 100);
}
constraint FOOBStar
{
// apply a specific constraint to all nodes beginning with “B” - use wildcard
in Structural models("ProcessingCompound")->
select(p | p.name() == "B*")->PowerStrategy(1, 100);
}
Specification Aspects
Comp1package org.apache.tomcat.session;
import org.apache.tomcat.core.*;import org.apache.tomcat.util.StringManager;import java.io.*;import java.net.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;
/** * Core implementation of a server session * * @author James Duncan Davidson [[email protected]] * @author James Todd [[email protected]] */
public class ServerSession {
private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private Hashtable values = new Hashtable(); private Hashtable appSessions = new Hashtable(); private String id; private long creationTime = System.currentTimeMillis();; private long thisAccessTime = creationTime; private long lastAccessed = creationTime; private int inactiveInterval = -1; ServerSession(String id) { this.id = id; }
public String getId() { return id; }
public long getCreationTime() { return creationTime; }
public long getLastAccessedTime() { return lastAccessed; } public ApplicationSession getApplicationSession(Context context, boolean create) { ApplicationSession appSession = (ApplicationSession)appSessions.get(context);
if (appSession == null && create) {
// XXX // sync to ensure valid? appSession = new ApplicationSession(id, this, context); appSessions.put(context, appSession); }
// XXX // make sure that we haven't gone over the end of our // inactive interval -- if so, invalidate and create // a new appSession return appSession; } void removeApplicationSession(Context context) { appSessions.remove(context); }
/** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */
void accessed() { // set last accessed to thisAccessTime as it will be left over // from the previous access
lastAccessed = thisAccessTime; thisAccessTime = System.currentTimeMillis(); }
void validate()
void validate() { // if we have an inactive interval, check to see if // we've exceeded it
if (inactiveInterval != -1) { int thisInterval = (int)(System.currentTimeMillis() - lastAccessed) / 1000;
if (thisInterval > inactiveInterval) { invalidate();
ServerSessionManager ssm = ServerSessionManager.getManager();
ssm.removeSession(this); } } }
synchronized void invalidate() { Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ApplicationSession appSession = (ApplicationSession)appSessions.get(key);
appSession.invalidate(); } } public void putValue(String name, Object value) { if (name == null) { String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg); }
removeValue(name); // remove any existing binding values.put(name, value); }
public Object getValue(String name) { if (name == null) { String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg); }
return values.get(name); }
public Enumeration getValueNames() { return values.keys(); }
public void removeValue(String name) { values.remove(name); }
public void setMaxInactiveInterval(int interval) { inactiveInterval = interval; }
public int getMaxInactiveInterval() { return inactiveInterval; }
// XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. synchronized void reap() { Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ApplicationSession appSession = (ApplicationSession)appSessions.get(key);
appSession.validate(); } }}
Quantification Over Base Code (AOP)
Weavers ‘instrument’ code with advice
pointcut pubIntf(Object o): call(public * com.borland.*.*(..)) && target(o);
after(Object o) throwing (Error e): pubIntf(o) { log.write(o, e); … }
after(Object o) throwing (Error e): pubIntf(o) { log.write(o, e); … }
Comp2package org.apache.tomcat.session;
import org.apache.tomcat.util.*;import org.apache.tomcat.core.*;import java.io.*;import java.net.*;import java.util.*;import javax.servlet.http.*;
/** * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */
public class ServerSessionManager implements SessionManager {
private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private static ServerSessionManager manager; // = new ServerSessionManager();
protected int inactiveInterval = -1;
static { manager = new ServerSessionManager(); }
public static ServerSessionManager getManager() { return manager; }
private Hashtable sessions = new Hashtable(); private Reaper reaper;
private ServerSessionManager() { reaper = Reaper.getReaper(); reaper.setServerSessionManager(this); reaper.start(); }
public void accessed( Context ctx, Request req, String id ) { ApplicationSession apS=(ApplicationSession)findSession( ctx, id); if( apS==null) return;
ServerSession servS=apS.getServerSession(); servS.accessed(); apS.accessed();
// cache it - no need to compute it again req.setSession( apS ); }
public HttpSession createSession(Context ctx) { String sessionId = SessionIdGenerator.generateId(); ServerSession session = new ServerSession(sessionId); sessions.put(sessionId, session);
if(-1 != inactiveInterval) { session.setMaxInactiveInterval(inactiveInterval); } return session.getApplicationSession( ctx, true ); }
public HttpSession findSession(Context ctx, String id) { ServerSession sSession=(ServerSession)sessions.get(id); if(sSession==null) return null;
return sSession.getApplicationSession(ctx, false); }
// XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later.
synchronized void reap() { Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key);
session.reap(); session.validate(); } }
synchronized void removeSession(ServerSession session) { String id = session.getId();
session.invalidate(); sessions.remove(id); }
public void removeSessions(Context context) { Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key); ApplicationSession appSession = session.getApplicationSession(context, false);
if (appSession != null) { appSession.invalidate(); } } }
/** * Used by context to configure the session manager's inactivity timeout. * * The SessionManager may have some default session time out, the * Context on the other hand has it's timeout set by the deployment * descriptor (web.xml). This method lets the Context conforgure the * session manager according to this value. * * @param minutes The session inactivity timeout in minutes. */ public void setSessionTimeOut(int minutes) { if(-1 != minutes) { // The manager works with seconds... inactiveInterval = (minutes * 60); } }}
Comp1package org.apache.tomcat.session;
import org.apache.tomcat.core.*;import org.apache.tomcat.util.StringManager;import java.io.*;import java.net.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;
/** * Core implementation of a server session * * @author James Duncan Davidson [[email protected]] * @author James Todd [[email protected]] */
public class ServerSession {
private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private Hashtable values = new Hashtable(); private Hashtable appSessions = new Hashtable(); private String id; private long creationTime = System.currentTimeMillis();; private long thisAccessTime = creationTime; private long lastAccessed = creationTime; private int inactiveInterval = -1; ServerSession(String id) { this.id = id; }
public String getId() { return id; }
public long getCreationTime() { return creationTime; }
public long getLastAccessedTime() { return lastAccessed; } public ApplicationSession getApplicationSession(Context context, boolean create) { ApplicationSession appSession = (ApplicationSession)appSessions.get(context);
if (appSession == null && create) {
// XXX // sync to ensure valid? appSession = new ApplicationSession(id, this, context); appSessions.put(context, appSession); }
// XXX // make sure that we haven't gone over the end of our // inactive interval -- if so, invalidate and create // a new appSession return appSession; } void removeApplicationSession(Context context) { appSessions.remove(context); }
/** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */
void accessed() { // set last accessed to thisAccessTime as it will be left over // from the previous access
lastAccessed = thisAccessTime; thisAccessTime = System.currentTimeMillis(); }
void validate()
void validate() { // if we have an inactive interval, check to see if // we've exceeded it
if (inactiveInterval != -1) { int thisInterval = (int)(System.currentTimeMillis() - lastAccessed) / 1000;
if (thisInterval > inactiveInterval) { invalidate();
ServerSessionManager ssm = ServerSessionManager.getManager();
ssm.removeSession(this); } } }
synchronized void invalidate() { Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ApplicationSession appSession = (ApplicationSession)appSessions.get(key);
appSession.invalidate(); } } public void putValue(String name, Object value) { if (name == null) { String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg); }
removeValue(name); // remove any existing binding values.put(name, value); }
public Object getValue(String name) { if (name == null) { String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg); }
return values.get(name); }
public Enumeration getValueNames() { return values.keys(); }
public void removeValue(String name) { values.remove(name); }
public void setMaxInactiveInterval(int interval) { inactiveInterval = interval; }
public int getMaxInactiveInterval() { return inactiveInterval; }
// XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. synchronized void reap() { Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ApplicationSession appSession = (ApplicationSession)appSessions.get(key);
appSession.validate(); } }}
Comp2package org.apache.tomcat.session;
import org.apache.tomcat.util.*;import org.apache.tomcat.core.*;import java.io.*;import java.net.*;import java.util.*;import javax.servlet.http.*;
/** * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */
public class ServerSessionManager implements SessionManager {
private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private static ServerSessionManager manager; // = new ServerSessionManager();
protected int inactiveInterval = -1;
static { manager = new ServerSessionManager(); }
public static ServerSessionManager getManager() { return manager; }
private Hashtable sessions = new Hashtable(); private Reaper reaper;
private ServerSessionManager() { reaper = Reaper.getReaper(); reaper.setServerSessionManager(this); reaper.start(); }
public void accessed( Context ctx, Request req, String id ) { ApplicationSession apS=(ApplicationSession)findSession( ctx, id); if( apS==null) return;
ServerSession servS=apS.getServerSession(); servS.accessed(); apS.accessed();
// cache it - no need to compute it again req.setSession( apS ); }
public HttpSession createSession(Context ctx) { String sessionId = SessionIdGenerator.generateId(); ServerSession session = new ServerSession(sessionId); sessions.put(sessionId, session);
if(-1 != inactiveInterval) { session.setMaxInactiveInterval(inactiveInterval); } return session.getApplicationSession( ctx, true ); }
public HttpSession findSession(Context ctx, String id) { ServerSession sSession=(ServerSession)sessions.get(id); if(sSession==null) return null;
return sSession.getApplicationSession(ctx, false); }
// XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later.
synchronized void reap() { Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key);
session.reap(); session.validate(); } }
synchronized void removeSession(ServerSession session) { String id = session.getId();
session.invalidate(); sessions.remove(id); }
public void removeSessions(Context context) { Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key); ApplicationSession appSession = session.getApplicationSession(context, false);
if (appSession != null) { appSession.invalidate(); } } }
/** * Used by context to configure the session manager's inactivity timeout. * * The SessionManager may have some default session time out, the * Context on the other hand has it's timeout set by the deployment * descriptor (web.xml). This method lets the Context conforgure the * session manager according to this value. * * @param minutes The session inactivity timeout in minutes. */ public void setSessionTimeOut(int minutes) { if(-1 != minutes) { // The manager works with seconds... inactiveInterval = (minutes * 60); } }}
Quantification Over a Domain Model (AODM)
Apply AO Weaving concepts to Model-based systems Weavers ‘Decorate’ Models with attributes &
constraints Weavers compose new model constructs
…select(p | p.name() == “Model*” && p.kind() == “StateFlow”)->Strategy3();…
Strategy1
Strategy2
Strategy3
StrategyN
The Metaweaver Framework
Strategy Code
Generator
Strategies (C++)
Strategy Specifications
Specification
Aspects
strategy ApplyConstraint(constraintName : string, expression : string)
{
addAtom("OCLConstraint", "Constraint", constraintName).addAttribute("Expression", expression);
}
strategy RemoveConstraint(constraintName : string)
{
findAtom(constraintName).removeChild();
}
strategy ReplaceConstraint(constraintName : string, expression : string)
{
RemoveConstraint(constraintName);
ApplyConstraint(constraintName, expression);
}
XML
(Model Hierarchy)
Embedded Constraint Language
Arithmetic Operators +, -, *, /, =, <, >, <=, >=, <>
Logical Operatorsand, or, xor, not, implies, if/then/else
Collection Operator ->
Property Operator .
Standard OCL Collection Operators
collection->size() : integercollection->forAll( x | f(x) ) : Booleancollection->select( x | f(x) ) : collectioncollection->exists( x | f(x) ) : Boolean
Included OCL Operators
Embedded Constraint Language (ECL) Traditional OCL has been strictly a declarative query
language New uses require an imperative procedural style Addition of side effects into model
Examples: addAtom(…), findAtom(…) addAttribute(…), findAttribute(…) removeNode(…)
Support for recursion Chaining of strategies (procedure calls)
Inlined C++ code
Generation of ECL
…components.models("")->select(c |c.id() == refID)->DetermineLaziness();…
CComPtr<IXMLDOMNodeList> mods=XMLParser::models(components,"");nodeTypeVector selectVec1 = XMLParser::ConvertDomList(mods);nodeTypeVector selectVecTrue1 = new std::vector<nodeType>;vector<nodeType>::iterator itrSelect1;for(itrSelect1 = selectVec1->begin(); itrSelect1 != selectVec1->end(); itrSelect1++) { nodeType selectNode1 = (*itrSelect1); nodeType c; c = selectNode1; CComBSTR id0 = XMLParser::id(c); ClData varforward1(id0); ClData varforward2(refID); bool varforward3 = varforward1 == varforward2; if(varforward3) selectVecTrue1->push_back(*itrSelect1);}
vector<nodeType>::iterator itrCollCall1;for(itrCollCall1 = selectVecTrue1->begin(); itrCollCall1 != selectVecTrue1->end(); itrCollCall1++) DetermineLaziness::apply(…);
Achieved Goals - AODM
Using a DSL, the modeler is shielded from the details of the core XML Document Object Model (DOM) API calls.
Using a DSL, the specification of the navigation within the domain models (while performing transformations) is raised to a higher level of abstraction
MSF to Generated Code
Lines of Code Bytes of Code Advise
MSF: 33 C++: 506
Ratio: 1::15.33
MSF: 761b C++: 14.79k
Ratio: 1::19.44 Relex
MSF: 34 C++: 538
Ratio: 1::15.82
MSF: 819b C++: 17.54k
Ratio: 1::21.42 FMECA
MSF: 44 C++: 802
Ratio: 1::18.22
MSF: 1.26k C++: 27.32k
Ratio: 1::21.68 AEFR
MSF: 49 C++: 639
Ratio: 1::13.04
MSF: 870b C++: 21.42k
Ratio: 1::24.62 GME
MSF: 58 C++: 922
Ratio: 1::15.89
MSF: 1.19k C++: 28.71k
Ratio: 1::24.13
TVL to Generated Code
Lines of Code Bytes of Code Advise2IMS
TVL: 155 C++: 355
Ratio: 1::2.29
TVL: 4.03k C++: 8.78k
Ratio: 1::2.18 Relex2IMS
TVL: 351 C++: 523
Ratio: 1::1.49
TVL: 10.15k C++: 17.54k Ratio: 1::1.73
FMECA2IMS
TVL: 248 C++: 435
Ratio: 1::1.75
TVL: 7.85k C++: 12.10k Ratio: 1::1.54
AEFR2IMS
TVL: 192 C++: 497
Ratio: 1::2.59
TVL: 6.49k C++: 13.39k Ratio: 1::2.06
GME2IMS
TVL: 251 C++: 523
Ratio: 1::2.08
TVL: 7.22k C++: 14.27k Ratio: 1::1.98
ECL to Generated Code Lines of
Code Bytes of Code
Power Distribution
ECL: 43 C++: 140
Ratio: 1::3.25
ECL: 859b C++: 3.08k
Ratio: 1::3.50
Processor Assignment
ECL: 39 C++: 137
Ratio: 1::3.50
ECL: 954b C++: 3.28k
Ratio: 1::3.44
Eager/Lazy
ECL: 85 C++: 230
Ratio: 1::2.71
ECL: 2.03k C++: 6.24k
Ratio: 1::3.07
Exhaustive State
Transition
ECL: 70 C++: 184
Ratio: 1::2.62
ECL: 1.92k C++: 5.14k
Ratio: 1::2.68
State Generation
ECL: 128 C++: 242
Ratio: 1::1.89
ECL: 3.42k C++: 6.76k
Ratio: 1::1.98
Concluding Remarks-Benefits of DSL use in these Two Projects The tedious and mundane parts of writing a program
are automated in the translation from the DSL to a traditional programming language.
Repetitive code sequences are generated automatically instead of the error-prone manual cut-and-paste method. The generation of such tedious code also has advantages in the maintenance phase of a project’s lifecycle.
Solutions can be constructed quickly because the programmer can more easily focus on the key abstractions. A DSL hides the underlying details of the solution space as implemented in a traditional programming language.
Concluding Quotes
Niklaus Wirth: We must recognize the strong and undeniable influence that our language exerts on our ways of thinking and, in fact, delimits the abstract space in which we can formulate – give form to – our thoughts.
George Polya: An important step in solving a problem is to choose the notation. It should be done carefully. The time we spend now on choosing the notation may be well repaid by the time we save later avoiding hesitation and confusion. Moreover, choosing the notation carefully, we have to think sharply of the elements of the problem which must be denoted. Thus, choosing a suitable notation may contribute essentially to understanding the problem.
Ted Biggerstaff: The first order term in the success equation of reuse is the amount of domain-specific content and the second order term is the specific technology chosen in which to represent that content.
Evaluating Tool Integration Solutions
How much time and effort does it cost to integrate a new tool?
How scalable is the integration approach? How much expert knowledge is needed to
realize an integration solution? What is the coupling between the individual
tools and the integration technology?
Development Effort
Translators can be written within a few man-days Average translator was 225 lines of traversal/visitor code
Tool Adapter development depends on: Complexity of tool Complexity of the tool’s data access mechanism
(e.g., ADO, COM, comma separated values) Developer experience with previous Tool Adapters
Our average development time for a bi-directional Tool Adapter is about 10 person-weeks