Wiki
Clone wikitouchram / developers / architecture-overview
TouchCORE Architecture
Main TouchCORE Architecture
The dotted arrows represent dependencies between components (unless specified otherwise on the arrow).
Quick Overview of Components
-
Model: Contains the metamodel, diagrams of the metamodel and the generated code of it.
-
Edit: Contains the generated edit code and includes adjustments to filter choice of values and property texts/labels.
-
Controller: The controllers to perform command-based modifications on the model.
-
GUI: The graphical user interface. General components that extend the ones from MT4j. Views for visualizing the model. Handlers to handle the events triggered by the user.
Architecture with Additional Components
The dotted arrows represent dependencies between components (unless specified otherwise on the arrow).
Quick Overview of Components
- Weaver: The RAM weaver and individual weavers for each view.
- Generator: The code generator.
- Validator: The validator to validate the models using OCL constraints.
- Classloader: The class loader to load external classes from frameworks or libraries to import them into a model (as implementation classes).
Base Package
The base package is ca.mcgill.sel
with .core
for CORE and .ram
for RAM, followed by a subpackage based on the project (see architecture overview).
Important Classes
- All classes in
ca.mcgill.sel.commons
(e.g., for string and file manipulation etc.) ca.mcgill.sel.commons.ResourceUtil
for loading resources independent of where the code is run from (e.g., IDE, JAR etc.)- The following classes in
ca.mcgill.sel.commons.emf.util
ResourceManager
to load and save any model based on an ecore metamodelEMFModelUtil
for generic helper methods for any model based on an ecore metamodelEMFEditUtil
for generic helper methods for EMF.Edit purposes for any model based on an ecore metamodel
ca.mcgill.cs.sel.ram.util.RAMModelUtil
for convenience methods dealing with the modelca.mcgill.sel.core.util.COREModelUtil
for convenience methods dealing with a CORE modelca.mcgill.cs.sel.ram.provider.util.RAMEditUtil
for convenience methods dealing with edit-related functionality (e.g., filtering)- Classes in
ca.mcgill.ram.utils
:- constant classes for colors, constants, fonts, icons and regular expressions
RamModelUtils
for view-related convenience methods dealing with a model
ca.mcgill.sel.ram.weaver.util
classesWeavingInformation
andReferenceUpdater
. See Weaver page.
The following classes provide helper methods and should be used when possible.
Important Details for Implementation
Initializing Views, Destroying and Reacting to Modifications
When developing, the following "cycle" needs to used in order to fully support notifications, undo/redo and avoiding memory leaks.
- The view is initialized with the current existing model state.
- The view registers itself (implement
INotifyChangedListener
) on the item provider usingEMFEditUtil.addListenerFor(...)
INotifyChangedListener.notifyChanged(...)
needs to make sure that the notifier of the notification is the object that the view visualizes, because the item providers are usually stateless (hence, notifications are received for all objects of the same type)- check for the
EStructuralFeature
the notification is for that is of interest (e.g.,RamPackage.Literals.CLASSIFIER__OPERATIONS
for operations of the classifier) - ADD and REMOVE need to be supported for undo/redo (this should reuse the same methods to build the view during initialization)
- SET for references with upper bound of 1
- If you do not receive notifications for a certain feature, check the genmodel (see page linked below).
- See GUI page for more information.
- check for the
- the view needs to override
destroyComponent()
and unregister the listener usingEMFEditUtil.removeListenerFor(...)
- In case there are any view components (or any other resources) that are currently not visible (i.e., not a children), they need to be destroyed specifically (call
destroy()
). Otherwise memory leaks occur, since the resources are not freed by the garbage collector.
Handling of Events
- Each view that events (from the user) can occur on should have a handler.
- The handler is the one handling the event and taking corresponding action, such as requesting the controller to update/change the model or modifying the view.
- Do not directly change the model, always use commands and react to notifications.
- Some more information is provided on the GUI page.
Updating the Model using Commands
- Only use commands to change the model.
- For simple changes, the corresponding methods of the super-class
BaseController
can be used (for example,doAdd(...)
) - For complex changes, a
CompoundCommand
needs to be used in order to properly support undo/redo to undo/redo all changes as one. - The order of the commands matters, it has to be logical by first adding independent objects and then adding those (or setting something) that depend on the previous added objects etc.
- Therefore, the remove operation needs to do the reverse of the add operation (e.g.,
addOperation
andremoveOperation
) - This also allows to consistently react in the view to those changes. The commands of a compound command will trigger each a notification.
- Some additional information is provided on the Controllers page.
Using ItemProviders
The TextView
and SelectorView
(or their handlers more specifically) are able to retrieve information from the item providers (which get it from the model) regarding the textual representation of features (as in a structural feature of the (meta)model [EStructuralFeature
]). Furthermore, when modifying a reference, the choice of possible values is retrieved from the property descriptor for that structural feature from the item provider.
Therefore, when the default representation or choice of values are not satisfactory, the change should be performed in the corresponding item provider. This allows this information to be UI-independent, such that a different view provides the same information. One example is the generated editor.
- First, mark the
getText(...)
oraddXXXPropertyDescriptor
operation with@generated NOT
to prevent the EMF code generator from overwriting any modifications - The default textual representation of an object (when showing the full label) is retrieved from
getText(...)
- The choice of values is retrieved from the property descriptor
- change the call from
createItemPropertyDescriptor
tonew ItemPropertyDescriptor
and overwritegetChoiceOfValues
in that inner class. - Either call
super.getChoiceOfValues(...)
and filter that (consider thatnull
is contained in that list) or create the list yourself.null
should always be part of the result to stay compliant with the default behaviour. - If the textual representation of the choices is not satisfactory, additionally overwrite
getLabelProvider(...)
and return anew IItemLabelProvider
and implement its methods. Consider thatnull
might be passed as a parameter.
- change the call from
- Important: Always check that referenced elements are not null. This is never guaranteed!
- Some additional information is provided on the Edit page.
Resource Management
Resources need to be located such that independent of the time and place (e.g., development time in IDE, run time from JAR etc.) at which the application runs, the resource can be located. To do that, a source folder called resources
is used and the path can be obtained using ca.mcgill.sel.commons.ResourceUtil
. One nice side effect is that the resources will be automatically added to the JAR with the compiled source code (since they are located in a source folder).
Updated