3.2 Gathering Model Statistics
To gather the model’s 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
    • Provides model traversal methods (inherited from ForAllAObject).
    • Leverages the EMF switch class concept to allow the specification of processing methods for each class of object in the AADL model.
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.
3.2.1 Subclassing 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.
3.2.2 The ModelStatistics Code
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,
  • We provide specific instantiations of CoreSwitch, FlowSwitch, and InstanceSwitch. It is convenient to provide the switches using Java anonymous classes.
  • We only override those “case” methods that are specifically interesting to our analysis:
    • CoreSwitch.caseComponentType
    • CoreSwitch.caseComponentImpl
    • FlowSwitch.caseFlowSpec
    • FlowSwitch.caseEndToEndFlow
    • InstanceSwitch.caseComponentInstance
    • InstanceSwitch.caseConnectionInstance

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;
}
}
3.2.3 An Alternative Use of Switches
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 classes—in ComponentSwitch—except 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.