The Structure Language may sometimes be insufficient to express advanced constraints on the language structure. The Constraints aspect gives you a way to define such additional constraints.
Can be child/parent/ancestor/root
These are the first knobs to turn when defining constraints for a concept. They determine whether instances of this concept can be hooked as children (parents, ancestors) nodes of other nodes or root nodes in models.You specify them as boolean closures to invoke each time when evaluating allowed possition for a node in the AST.
can be child
Return false if an instance of the concept is not allowed to be a child of specific nodes.
parameter |
description |
---|---|
operationContext |
IOperationContext |
scope |
current context (IScope) |
parentNode |
the parent node we are checking |
childConcept |
concept of the child node (can be a subconcept of this concept) |
link |
LinkDeclaration of the child node (child role can be taken from there) |
can be parent
Return false if an instance of concept is not allowed to be a parent of specific concept node (in a given role).
parameter |
description |
---|---|
operationContext |
IOperationContext |
scope |
context (IScope) |
node |
the parent node we are checking (instance of this concept) |
childConcept |
the concept of the child node we are checking |
link |
LinkDeclaration of the child node |
can be ancestor
Return false if an instance of the concept is not allowed to be an ancestor of specific nodes.
parameter |
description |
---|---|
operationContext |
IOperationContext |
scope |
context (IScope) |
node |
the ancestor node we are checking (instance of this concept) |
childConcept |
the concept of the descendant node |
can be root
This constraint is available only for rootable concepts (instance can be root is true in the concept structure description). Return false if instance of concept cannot be a root in the given model.
parameter |
description |
---|---|
operationContext |
IOperationContext |
scope |
context (IScope) |
model |
model of the root |
Property constraints
Technically speaking, "pure" concept properties are not properties in its original meaning, but only public fields. Property constraints allow you to make them real properties. Using these constraints, the behavior of concept's properties can be customized. Each propertz constraint is applied to a single specified property.
property - the property to which this constraint is applied.
get - this method is executed to get property value every time property is accessed.
parameter |
description |
---|---|
node |
node to get property from |
scope |
context (IScope) |
set - this method is executed to set property value on every write. The property value is guaranteed to be valid.
parameter |
description |
---|---|
node |
node to set property |
propertyValue |
new property value |
scope |
context (IScope) |
is valid - this method should determine whether the value of the property is valid. This method is executed every time before changing the value, and if it returns false, the set() method is not executed.
parameter |
description |
---|---|
node |
node to check property |
propertyValue |
value to be checked |
scope |
context (IScope) |
Referent constraints
Constraints of this type help to add behavior to concept's links and make them look more properties-like.
referent set handler - if specified, this method is executed on every set of this link.
parameter |
description |
---|---|
referenceNode |
node that contains link. |
oldReferentNode |
old value of the reference. |
newReferentNode |
new value of the reference. |
scope |
context: IScope interface to object that shows you models, languages and devkits you can see from the code. |
search scope - defines the set of nodes to which this link can point. The method can return either a sequence<node<>> or ISearchScope.
parameter |
description |
---|---|
model |
the model that contains the node with the link. This is included for convenience, since both referenceNode and enclosingNode keep the model too. |
scope |
IScope interface that shows models, languages and devkits you can see from the code. |
referenceNode |
the node that contains the actual link. It can be null when a new node is being created for a concept with smart reference. In this situation smart reference is used to determine what type of node to create in the context of enclosingNode, so the search scope method is called with a null referenceNode. |
enclosingNode |
parent of the node that contains the actual link, null for root nodes. Both referenceNode and enclosingNode cannot be null at the same time. |
linkTarget |
the concept that this link can refer to. Usually it is a concept of the reference, so it is known statically. If we specialize reference in subconcept and do not define search scope for specialized reference, then linkTarget parameter can be used to determine what reference specialization is required. |
If search scope is not set for the reference then default scope from the referenced concept is used. If the default search scope is also not set then "global" scope is used: all instances of referent concept from all imported models.
validator (since 1.5) - Each reference is checked against it's search scope and if, after changes in the model, a reference ends up pointing out of the search scope, MPS marks such a reference with an error message. Sometimes it is not efficient to build the whole search scope just to check if the reference is in scope. The search scope can be big or it may be much easier to check if the given node is in scope than to calculate what nodes are in scope. You can create quick reference check procedure here to speed up reference validation in such situations.
parameter |
description |
---|---|
model |
context of reference usage, the same meaning as in the search scope method. The main difference: referenceNode cannot be null here because validator is not used during node creation. |
checkedNode |
the node to be validated (referenceNode has a reference to checkedNode of type linkTarget) |
It is possible to create validation routine only if you have created a list of nodes in the corresponding search scope. If ISearchScope is returned from search scope method, then isInScope(SNode) method of ISearchScope interface will be used for validation, you should override this method with your validation routine.
It is not possible to define validation routine without defining search scope.
presentation - here you specify how the reference will look like in the editor and in the completion list. Sometimes it is convenient to show reference differently depending on context. For example, in Java all references to an instance field f should be shown as this.f, if the field is being shadowed by the local variable declaration with the same name. By default, if no presentation is set, the name of the reference node will be used as its presentation (provided it is an INamedConcept).
parameter |
description |
---|---|
model |
the context of reference usage, the same meaning as in the search scope function. |
parameterNode |
the node to be presented (referenceNode has a reference to parameterNode of type linkTarget) |
visible |
true - presentation of existing node, false - for new node (to be created after selection in completion menu) |
smartReference |
true - node is presented in the smart reference |
inEditor |
true - presentation for editor, false - for completion menu |
ISearchScope
Low level interface that can be implemented to support search scope. We recommend to subclass AbstractSearchScope
(implements ISearchScope
) abstract class instead of direct implementation of ISearchScope
interface. The only abstract method in AbstractSearchScope
class is
@NotNull List<SNode> getNodes(Condition<SNode> condition)
- return list of nodes in the current search scope satisfying condition
, similar to search scope but with the additional condition.
Other useful methods to override:
boolean isInScope(SNode node)
- the same function as in validator method.
IReferenceInfoResolver getReferenceInfoResolver(SNode, AbstractConceptDeclaration);
resolve info - TODO
Default scope
Suppose we have a link pointing to an instance of concept C and we have no #search scope defined for this link in referent constraints. When you edit this link, all instances of concept C from all imported models are visible by default. If you want to restrict set of visible instances for all links to concept C you can set default scope for the concept. As in referent constraint you can set search scope, validator and presentation methods. All the parameters are the same.
2 Comments
Francois Genolini
How do you change the default scope?
I have created 2 sandboxes in the solution, and the second solution's generated field declaration start numbering where the first one left off.
First sandbox has
inputField0
inputField1
and the second sandbox has:
inputField2
what I was expecting was
inputField0
Mihail Muhin
If I hear you aright, you are talking about Calculator tutorial and generated field declarations - this has nothing in common with scopes.
Scopes are used mainly while editing source code; generated field declarations just have unique names. For now, unique names are produced by generation in "per-session" manner. So, If you generate the models together (in one generation session), names will be unique throughout all models generated in this session.