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())); } } } }