![]() ![]() |
||||
|
||||
To
gather the models statistics, we must traverse the AADL specification or system instance
and count the occurrences of the various model elements. We create a subclass of
AadlProcessingSwitch for this purpose. This class
To
compute our model statistics, we are going to create a new subclass, ModelStatistics, that
defines our particular statistics gathering functionality. But first we must take a closer look
at
class AadlProcessingSwitch.
Refer
to Javadoc of ForAllAObject and AadlProcessingSwitch. |
||||
The
OSATE framework package edu.cmu.sei.aadl.model.util includes an abstract class
ForAllAObject that defines methods for traversing AADL models but that leaves the method
of processing abstract. Traversal methods include, for example, top-down and bottom-up
traversals, as well as method to process lists of model objects. The traversal methods can
operate on both declarative and instance models; in the case of instance models, the traversal
can be scoped to the full model or only a modal subset. The particular modal subset is
determined by the system operational mode (SOM) iteration mechanism (see xxx). The
traversal methods abstract away the declarative/instance/modal distinctions by using the method
AObject.getChildren, which encapsulates how the children of a model element are
determined. Traversals invoke the processing method process(AObject). The default
implementation of process implements functionality for filtering model elements (see xxx) via
the suchThat method.
Class AadlProcessingSwitch,
also in edu.cmu.sei.aadl.model.util, is a subclass of
ForAllAObject that overrides the process method to leverage the EMF switch
class
concept to define a processing method for all abstract and concrete classes in the AADL meta
model (see
EMF book page xxx).
Specifically, for each AADL meta model package, there is
an EMF switch class that contains a case method for each class declared in the
package.
The AADL meta model is described using seven EMF packages: core, component,
connection, feature, flow, instance, and property. Thus there
are seven switch classes for
AADL models. For example, the AADL component types are modeled by classes in the EMF
Ecore package component. Thus there is a class ComponentSwitch, in Java package
edu.cmu.sei.aadl.model.component.util with methods caseBusType(BusType),
caseComponentClassifier(ComponentClassifier), caseDataImpl(DataImpl), etc.
When
a switch class instance is invoked on a model object using doSwitch(EObject), control
flows to the appropriate case method based on the class of the object. The switch
class
respects the class hierarchy, and invokes the most specific case method first, falling through
to case methods for super classes as long as null is returned. The final case
method is
defaultCase(EObject). For example, when given a SystemType instance, doSwitch invokes
the methods, caseSystemType, caseComponentType, caseSystemClassifier,
caseComponentClassifier, caseClassifier, casePropertyHolder, caseNamedElement,
caseAObject, and defaultCase, in order, until one of them returns a non-null value.
The
default implementation of each case method does nothing and returns null. This
ensures
that the default behavior is for control to flow to the first ancestor case that defines
a non-
trivial processing method. The constants AadlProcessingSwitch.NOT_DONE and
AadlProcessingSwitch.DONE provide symbolic names for null and a canonical non-null
value, respectively, to be used as return values from case methods.
As
there is a switch class for each Ecore package making up the AADL meta model, the
AadlProcessingSwitch.process(AObject) method is implemented by delegating to an
instance of the appropriate switch class based on the class of the given model object.
The
class declares a field for each switch class, e.g., componentSwitch, flowSwitch,
propertySwitch, etc. Subclasses customize the processing by overriding the method
initSwitches to assign instances of analysis specific subclasses of switch classes
to these
fields.
Finally,
a note on using instances of ForAllAObject and its subclasses. Instances of the class
maintain state during traversals (see Section 4.3 AADL Model Traversal Support).
Therefore, a new instance should always be created for each traversal. |
||||
The
class ModelStatisticsextends AadlProcessingSwitch and declares fields to count the
number of occurrences of component types, component implementations, flow specifications,
thread instances, process instances, etc. These fields are incremented as a side effect of the
model traversal process that it defines. To gather the statistics for a particular model, the
analysis creates a new instance of ModelStatistics, and then invokes a traversal. For this
simple analysis it does not matter whether a pre-order or a post-order traversal is used. But
some analyses might only work correctly if a specific traversal method is used. Thus it is a
good idea to document in the class’s Javadoc which traversal method is expected to be used.
Analysis results are retrieved using additional methods defined in ModelStatistics.
The
class ModelStatisticsis shown below. The actual behavior of the model traversal is
declared in the implementation of the method initSwitches, which initializes the switch fields
declared in the super class AadlProcessingSwitch. In particular,
In
the implementation below, we implement the method CoreSwitch.caseComponentType
which is called by the switch for each element that is a subtype of ComponentType.
ComponentType is actually an abstract class in the Ecore model and is the parent class for
each specific component type. In our implementation, we further test the model element against
the specific type DataType. We later discuss an alternative implementation approach that
avoids this testing inside of ComponentType.
The
AADL instance model uses ComponentInstance objects to model all categories of
instantiated components. Thus the InstanceSwitchcannot distinguish among the different
categories of instantiated components. Instead we use a Java switch statement within
caseComponentInstance to make this distinction:
switch (obj.getCategory().getValue())
{ ... }
The ComponentInstance.getCategory
method returns an instance of ComponentCategory
that indicates the component’s category. ComponentCategory implements the EMF
enumeration pattern, [see XXX]. In particular, enumerations provide a canonical object as well
as a canonical integer value for each element in the enumeration. The integer value for a
particular enumeration element is retrieved using getValue.
package edu.cmu.sei.osate.statistics;
import edu.cmu.sei.aadl.model.component.DataType;
import edu.cmu.sei.aadl.model.core.ComponentImpl;
import edu.cmu.sei.aadl.model.core.ComponentType;
import edu.cmu.sei.aadl.model.core.util.CoreSwitch;
import edu.cmu.sei.aadl.model.flow.EndToEndFlow;
import edu.cmu.sei.aadl.model.flow.FlowSpec;
import edu.cmu.sei.aadl.model.flow.util.FlowSwitch;
import edu.cmu.sei.aadl.model.instance.ComponentInstance;
import edu.cmu.sei.aadl.model.instance.ConnectionInstance;
import edu.cmu.sei.aadl.model.instance.util.InstanceSwitch;
import edu.cmu.sei.aadl.model.property.ComponentCategory;
import edu.cmu.sei.aadl.model.util.AadlProcessingSwitch;
public class ModelStatistics extends
AadlProcessingSwitch {
/* Counters to keep track
of occurrences of different
* objects in the model.
*/
private int typeCount = 0;
private int componentTypeCount
= 0;
private int compImplCount =
0;
private int threadCount = 0;
private int processCount = 0;
private int processorCount =
0;
private int busCount = 0;
private int deviceCount = 0;
private int memoryCount = 0;
private int componentCount =
0;
private int connectionCount
= 0;
private int flowCount = 0;
private int endToEndFlowCount
= 0;
public ModelStatistics() {
super();
}
protected final void initSwitches()
{
/*
We overwrite the case method for a class in the meta model
* specific switches.
*/
// The core switch
handles items from the Core package
coreSwitch = new
CoreSwitch() {
/* We want to count all component type declarations.
* Thus, we count instances of the abstract ComponentType class.
*/
public
Object caseComponentType(ComponentType obj) {
typeCount++;
// Only count those that are not DataType
if (!(obj instanceof DataType)) componentTypeCount++;
return DONE;
}
public
Object caseComponentImpl(ComponentImpl ci) {
compImplCount++;
return DONE;
}
};
// We want to
count flows, thus, we redefine the FlowSwitch
flowSwitch = new
FlowSwitch() {
public
Object caseFlowSpec(FlowSpec obj) {
flowCount++;
return DONE;
}
public
Object caseEndToEndFlow(EndToEndFlow obj) {
endToEndFlowCount++;
return DONE;
}
};
// We want to
count instance model objects.
instanceSwitch =
new InstanceSwitch() {
public
Object caseComponentInstance(ComponentInstance obj) {
componentCount++;
/* We want to count category specific instances. We retrieve
*
the category and branch on its numeric representation.
*/
switch (obj.getCategory().getValue()) {
case ComponentCategory.THREAD:
threadCount++;
return
DONE;
case ComponentCategory.PROCESS:
processCount++;
return
DONE;
case ComponentCategory.PROCESSOR:
processorCount++;
return
DONE;
case ComponentCategory.MEMORY:
memoryCount++;
return
DONE;
case ComponentCategory.BUS:
busCount++;
return
DONE;
case ComponentCategory.DEVICE:
deviceCount++;
return
DONE;
}
return DONE;
}
public
Object caseConnectionInstance(ConnectionInstance ci) {
connectionCount++;
return DONE;
}
};
}
public String getModelResult()
{
return "Model
Statistics: " + componentTypeCount
+ " component type declarations, " + compImplCount
+ " component implementation declarations, "
+ (typeCount - componentTypeCount)
+ " data type declarations. ";
}
public String getFlowResult()
{
return "Flow
Statistics: " + flowCount + " flow specifications, "
+ endToEndFlowCount + " end-to-end flows. ";
}
public String getApplicationResult()
{
if (componentCount
> 0) {
return
"Application statistics: " + threadCount + " threads, "
+
processCount + " processes, " + connectionCount
+
" semantic connections. ";
}
return null;
}
public String getExecutionPlatformResult()
{
if (componentCount
> 0) {
return
"Execution platform statistics: " + processorCount
+
" processors, " + memoryCount + " memory units, "
+
busCount + " buses, " + deviceCount + " devices.";
}
return null;
}
} |
||||
In
the method caseComponentType, above, we only increment componentTypeCount
when the model object is not a DataType. Rather than test the types in a conditional, we
could have achieved the same effect by overriding caseComponentType to only increment
typeCount while also overriding the case methods for all the specific component type
classesin ComponentSwitchexcept DataType to increment the componentTypeCount
field and return NOT_DONE. For example:
coreSwitch=newCoreSwitch(){
publicObjectcaseComponentType(ComponentTypeobj){ typeCount++; returnDONE; } }
componentSwitch
= new ComponentSwitch() {
public
Object caseSystemType(SystemType obj) {
componentTypeCount++;
// We want to fall through to caseComponentType
return NOT_DONE;
}
public
Object caseBusType(BusType obj) {
componentTypeCount++;
// We want to fall through to caseComponentType
return NOT_DONE;
}
//
Repeat for all component types except DataType
};
We
chose not to follow this implementation approach, however, because in addition to being
more verbose, it also makes the intent of the processing less clear. |
||||