Wiki

Clone wiki

touchram / developers / architecture-overview

TouchCORE Architecture

Main TouchCORE Architecture

TouchRAM 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

TouchRAM Architecture 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 metamodel
    • EMFModelUtil for generic helper methods for any model based on an ecore metamodel
    • EMFEditUtil 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 model
  • ca.mcgill.sel.core.util.COREModelUtil for convenience methods dealing with a CORE model
  • ca.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 classes WeavingInformation and ReferenceUpdater. 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.

  1. The view is initialized with the current existing model state.
  2. The view registers itself (implement INotifyChangedListener) on the item provider using EMFEditUtil.addListenerFor(...)
  3. 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.
  4. the view needs to override destroyComponent() and unregister the listener using EMFEditUtil.removeListenerFor(...)
  5. 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 and removeOperation)
  • 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(...) or addXXXPropertyDescriptor 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
    1. change the call from createItemPropertyDescriptor to new ItemPropertyDescriptor and overwrite getChoiceOfValues in that inner class.
    2. Either call super.getChoiceOfValues(...) and filter that (consider that null 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.
    3. If the textual representation of the choices is not satisfactory, additionally overwrite getLabelProvider(...) and return a new IItemLabelProvider and implement its methods. Consider that null might be passed as a parameter.
  • 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