Skip to content

Find other Model Elements

When implementing a model element converter or a writer, you regularly need information of model elements that you don’t have direct references to. There are several ways how you can programmatically find those model elements:

  • Navigate the object graph of model elements, starting from the model element that you do have a reference to. This is the most commonly used way to find other model elements.
  • Take the instance of the type com.gs.gapp.metamodel.basic.Model and try to find the model element that you need in its collection of model elements that you get with getElements().
  • Every model converter has a model conversion cache. That cache holds references to all model elements that get created by means of a model element converter. Use this mechanism if you want to implement some logic that depends on a subset of model elements that all are created on the same model converter.

Every model element has got relations to other model elements. Often this way of getting hold of individual other model elements is sufficient, at least when the model elements belong to the same metamodel. But what to do if you want to find out, which model element has triggered the creation of another model element that you have a reference to. This question is easy to answer by navigating the model element tree, which is covered in the next section.

The Model Element Tree

Every model element converter has an input model element and an output model element. Every model element converter creates a reference from the output model element to the corresponding input model element. At the same time, the output model element is added to the input model elements’ collection of output model elements. Its a collection since one and the same model element can be used as input model element for more than one model element converter. To be able to do all this, model element types need to implement ModelElementI. If a model element inherits from ModelElement, it automatically implements that interface. This universal mechanism creates a model element tree. That tree lets you find all model elements that ever were used in a generator (at the point in time when you navigate that tree).

ModelElementI provides some methods to navigate the model element tree. Methods whose names include ‘originating’ get model elements that served as input for model element converters that created ‘this’ model element. Methods whose names include ‘resulting’ or ‘extension’ get model elements that were created by model element converters where ‘this’ was the input model element. This naming is really useful when you use code proposal/completion functionality in your IDE.

[originating] → MODEL ELEMENT CONVERTER → [resulting/extension]

// -------------------------------------------------
// --- handling ORIGINATING model elements

Object getOriginatingElement();

T getOriginatingElement(Class<T> clazz);

Object getRootOriginatingElement();

boolean hasOriginatingElement(Serializable modelElement);

T getOriginatingElementDeeply(Class<T> type, boolean exactMatch);

// -------------------------------------------------
// --- handling RESULTING/EXTENSION model elements

Set<ModelElementI> getResultingModelElements();

Set<T> getResultingModelElements(Class<T> type);

T getSingleExtensionElement(Class<T> type);

T getSingleExtensionElement(Class<T> type, boolean exactMatch);

T getSingleExtensionElement(Class<T> type, boolean exactMatch, ConversionDetails conversionDetails);

T getSingleExtensionElementDeeply(Class<T> type);

T getSingleExtensionElementDeeply(Class<T> type, boolean exactMatch);

T1 getSingleExtensionElementDeeply(Class<T1> type, Class<T2> subsequentType, boolean exactMatch);

T getSingleExtensionElementDeeply(Class<T> type, ModelElementI potentialResultingElement, boolean exactMatch);

T1 getSingleExtensionElementDeeply(Class<T1> type, Class<T2> subsequentType, ModelElementI potentialResultingElement, boolean exactMatch);

List<T1> getAllExtensionElementDeeply(Class<T1> type, boolean exactMatch);

List<T1> getAllExtensionElementDeeply(Class<T1> type, Class<T2> subsequentType, ModelElementI potentialResultingElement, boolean exactMatch);

List<T3> collectExtensionElements(List<T3> resultList, Class<T3> type, Class<T4> subsequentType, ModelElementI potentialResultingElement, boolean exactMatch);

boolean hasExtensionElement(ModelElementI element);

Use the Model Converter’s “Model” Object

Every model converter has one instance of type Model. You get the model by calling getModel() from a model converter or model element converter. The “model” lets you get model elements by calling one of the following methods:

  • Set<ModelElementI> getElements()
  • <T extends ModelElementI> Set<T> getElements(Class<T> resultType)
  • <T extends ModelElementI> Set<T> getElementsSorted(Class<T> resultType)
  • <T extends ModelElementI> T getElement(Class<T> resultType)
  • <T extends ModelElementI> LinkedHashSet<T> getElementsFromSubsequentModels(Class<T> resultType)
  • <T extends ModelElementI> T getElementFromSubsequentModels(Class<T> resultType)
  • <T extends ModelElementI> LinkedHashSet<T> getElementsFromPrecedingModels(Class<T> resultType)
  • <T extends ModelElementI> T getElementFromPrecedingModels(Class<T> resultType)

A model converter’s “model” is supposed to be used by subsequent model converters. Only at the very end of a model conversion, the “model“‘s collection of model elements is filled up. In rare cases you can call boolean addElement(ModelElementI e) from one model element converter in order to make the element accessible from another model element converter.

The model element tree navigation functionality also works for the “model” objects:

protected void onConvert(S source, T target) {
    ...
    Model previousModel = getModel().getOriginatingElement(Model.class);
    for (AMetaClass modelElement : previousModel.getElements(AMetaClass.class)) {
        // do something with the model element
    }
    ...
}

You can even search the whole model element tree for elements of a specific metatype:

protected void onConvert(S source, T target) {
    ...
    Model rootModel = getModel().getRootModel();
    LinkedHashSet<MavenParentPom> parentPoms =
        this.getModel().getRootModel().getElementsFromSubsequentModels(MavenParentPom.class);
    ...
}

Use the Model Converter’s Conversion Cache

Every model converter has got a model conversion cache that holds references to all model elements that have been created during the execution of a model converter. Every created element is added to the cache right after onCreateModelElement has been called. Due to the order of execution of onCreateModelElement and onConvert, you can use that cache to get all model elements that have been created during the execution of a model converter. To really get all elements you have to access the cache from within onConvert. Otherwise, not all onCreateModelElement methods have been called before.

protected void onConvert(S source, T target) {
   ...
   for (ModelElementI modelElement : getConversionCache().values()) {
       if (modelElement instanceof ....) {
           // do something with the model element
       }
   }
   ...
}