The ElementId hierarchy provides the simplest base level of metamodel representation. The ElementIds feature
identity
uniqueness
thread-safety
predictability
hashcodes
Every primary hierachical metamodel object such as a Package, Type, Operation or Property has a globally unique identity established by the package-class-feature path.
Auxiliary metamodel object such as a TemplateParameter, TuplePart or List-of-Parameter have a locally unique identity supporting fast matching of tuples or single lookup for operation parameters.
ElementIds are unique, whereas metamodel elements are not; there may be many meta-models in many applications all with their own Boolean PrimitiveTypeImpl instances. The equivalence of these elements may be established rapidly since each returns the same TypeId.BOOLEAN singleton from PrimitiveTypeImpl.getTypeId().
Uniqueness of ElementIds is enforced by the various getXxxId methods of the single IdManager.INSTANCE. These methods are synchronized to ensure thread safety. Child hierarchical objects are similarly mediated by their parent.
CollectionTypeIds are a degenerate form of specialization/generalization with a single template parameter. The template parameter is declared explicitly in generalizations.
TupleTypes are self-contained, that is all external template parameter references with the part types are bindings of a specialized tuple type whose generalization replaces those external references by the template parameters of the generalization.
For instance given a declaration
Set(A)::op(B)() : Tuple(a:A, b:Bag(B), c:B)
Tuple(a:A, b:Bag(B), c:B) is the (A,B) specialization of the Tuple(T1,T2)(a:T1,b:Bag(T2),c:T2) generalization.
LambdaTypes are self-contained in the same way as tuples with specializations of generalizations.
A ParameterIds identifies an ordered list of typeid suitable for identifying an operation’s parameter list by a single object and hashcode.
A ParameterIds has no knowledge of its parent Operation and so ParameterIds are reused whenever the typeid list arises. Note that collection typeIds are always collectionTypeIds, so there is no need for multiplicities. The residual optional existence is not subject to overloading and is ignored in ParameterIds.
LambdaTypes reuse ParameterIds to capture the extended type list comprising the typeids of context-type, result-type then parameter-types.
A TuplePartId identifies a part of a Tuple. It has a name, typeid and index. The index is the part position in the set of parts in a parent tuple alphabetically sorted by name. It provides efficient access to a slot position in a tuple representation.
A TuplePartId has no knowledge of its parent Tuple and so TuplePartIds are reused whenever the same combination of name, typeid and index arise.
A TemplateParameterId identifies a template parameter in the ordered list of template parameters in a parent templateable element. It has just an index in the parent list. For debugging purposes a TemplateParameterId has a name such as $0 or $1.
A TemplateParameterId has no knowledge of its parent templateable element and so only a couple of TemplateParameterIds ever exist. Three are statically defined as TypeId.T_1, T_2, T_3.
TemplateParameterId has no knowledge of whether it is a type or value parameter. Pragmatically a TemplateParameterId extends a TypeId. (This design decision may need revision.)
Since the ElementIds are predictable and unique, code generation can assign them for computation once in static variables so that large parts of the costs of model elememnt location can be performed at compile time. At class load time it is only necessary to construct/share the ElementId object. At run-time the ElementId provides a hashcode to provode rapid lookup.