package jetbrains.mps.checkers;
/*Generated by MPS */
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.smodel.runtime.CheckingNodeContext;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.runtime.ConstraintsDescriptor;
import jetbrains.mps.smodel.language.ConceptRegistry;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.smodel.constraints.ModelConstraints;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import org.jetbrains.mps.openapi.language.SProperty;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.smodel.PropertySupport;
import org.jetbrains.mps.openapi.model.SNodeAccessUtil;
import jetbrains.mps.smodel.runtime.PropertyConstraintsDescriptor;
import jetbrains.mps.errors.messageTargets.PropertyMessageTarget;
public class ConstraintsChecker extends AbstractNodeChecker {
public ConstraintsChecker() {
}
/**
* XXX does anyone understand what's going on here? 'breaking node' suggests it's the place
* in user model (the one we check), while use of this pointer, as 'ruleNode' in LanguageErrorsComponent.addError
* makes me think it shall point to 'meta' object, rule/intention/constraint which caused the error.
*/
private SNodeReference getBreakingNodeAndClearContext(CheckingNodeContext checkingNodeContext) {
SNodeReference breakingNodePointer = checkingNodeContext.getBreakingNode();
checkingNodeContext.setBreakingNode(null);
return breakingNodePointer;
}
@Override
public void checkNode(final SNode node, LanguageErrorsCollector errorsCollector, SRepository repository) {
final SAbstractConcept nodeConcept = SNodeOperations.getConcept(node);
SNode parent = SNodeOperations.getParent(node);
ConstraintsDescriptor constraintsDescriptor = ConceptRegistry.getInstance().getConstraintsDescriptor(nodeConcept);
final CheckingNodeContext checkingNodeContext = new jetbrains.mps.smodel.runtime.impl.CheckingNodeContext();
if (parent != null) {
errorsCollector.addDependency(parent);
if (SNodeOperations.getConcept(parent).isValid()) {
SContainmentLink link = node.getContainmentLink();
if (link == null) {
errorsCollector.addError(node, "Incorrect child role used: LinkDeclaration with role \"" + SNodeOperations.getContainingLink(node).getName() + "\" was not found in parent node's concept: " + SNodeOperations.getConcept(parent).getName(), null);
return;
}
boolean canBeChild = errorsCollector.runCheckingAction(new _FunctionTypes._return_P0_E0<Boolean>() {
public Boolean invoke() {
return ModelConstraints.canBeChild(node, checkingNodeContext);
}
});
if (!(canBeChild)) {
SNodeReference rule = getBreakingNodeAndClearContext(checkingNodeContext);
errorsCollector.addError(node, "Node " + node + " cannot be child of node " + parent, rule);
}
}
}
if ((SNodeOperations.getParent(node) == null)) {
final SModel model = SNodeOperations.getModel(node);
boolean canBeRoot = errorsCollector.runCheckingAction(new _FunctionTypes._return_P0_E0<Boolean>() {
public Boolean invoke() {
return ModelConstraints.canBeRoot(nodeConcept, model);
}
});
if (!(canBeRoot)) {
SNodeReference rule = getBreakingNodeAndClearContext(checkingNodeContext);
errorsCollector.addError(node, "Not rootable concept added as root", rule);
}
}
if (!(SNodeOperations.getConcept(node).isValid())) {
errorsCollector.addError(node, "Concept of a node was not found", null);
}
for (final SNode child : SNodeOperations.getChildren(node)) {
boolean canBeParent = errorsCollector.runCheckingAction(new _FunctionTypes._return_P0_E0<Boolean>() {
public Boolean invoke() {
return ModelConstraints.canBeParent(child, checkingNodeContext);
}
});
if (!(canBeParent)) {
SNodeReference rule = getBreakingNodeAndClearContext(checkingNodeContext);
errorsCollector.addError(node, "Node " + node + " cannot be parent of node " + child, rule);
}
}
for (final Wrappers._T<SNode> ancestor = new Wrappers._T<SNode>(parent); ancestor.value != null; ancestor.value = SNodeOperations.getParent(ancestor.value)) {
boolean canBeAncestor = errorsCollector.runCheckingAction(new _FunctionTypes._return_P0_E0<Boolean>() {
public Boolean invoke() {
return ModelConstraints.canBeAncestorDirect(ancestor.value, node, checkingNodeContext);
}
});
if (!(canBeAncestor)) {
SNodeReference rule = getBreakingNodeAndClearContext(checkingNodeContext);
errorsCollector.addError(node, "Invalid ancestor: " + ancestor.value, rule);
}
}
// Properties validation
Iterable<SProperty> props = nodeConcept.getProperties();
for (final SProperty property : Sequence.fromIterable(props)) {
final PropertySupport ps = PropertySupport.getPropertySupport(property);
final String value = ps.fromInternalValue(SNodeAccessUtil.getProperty(node, property));
final PropertyConstraintsDescriptor propertyDescriptor = constraintsDescriptor.getProperty(property);
boolean canSetValue = (propertyDescriptor == null ? false : errorsCollector.runCheckingAction(new _FunctionTypes._return_P0_E0<Boolean>() {
public Boolean invoke() {
return ps.canSetValue(propertyDescriptor, node, property, value);
}
}));
if (!(canSetValue)) {
// TODO this is a hack for anonymous classes
if ("name".equals(property.getName()) && ("AnonymousClass".equals(nodeConcept.getName()) || "InternalAnonymousClass".equals(nodeConcept.getName()))) {
continue;
}
// todo find a rule
errorsCollector.addError(node, "Property constraint violation for property \"" + property.getName() + "\"", null, new PropertyMessageTarget(property.getName()));
}
}
}
}