blob: 54ae0b08372d68bf4b53c77e7bcd56e28a08f652 [file] [log] [blame]
/*
* generated by Xtext
*/
package org.eclipse.etrice.core.fsm.validation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.etrice.core.common.converter.BaseConverterService;
import org.eclipse.etrice.core.common.converter.CCStringIndentation;
import org.eclipse.etrice.core.fsm.fSM.ChoicePoint;
import org.eclipse.etrice.core.fsm.fSM.DetailCode;
import org.eclipse.etrice.core.fsm.fSM.FSMPackage;
import org.eclipse.etrice.core.fsm.fSM.InitialTransition;
import org.eclipse.etrice.core.fsm.fSM.ModelComponent;
import org.eclipse.etrice.core.fsm.fSM.NonInitialTransition;
import org.eclipse.etrice.core.fsm.fSM.RefinedState;
import org.eclipse.etrice.core.fsm.fSM.RefinedTransition;
import org.eclipse.etrice.core.fsm.fSM.SimpleState;
import org.eclipse.etrice.core.fsm.fSM.StateGraph;
import org.eclipse.etrice.core.fsm.fSM.StateGraphItem;
import org.eclipse.etrice.core.fsm.fSM.TrPoint;
import org.eclipse.etrice.core.fsm.fSM.Transition;
import org.eclipse.etrice.core.fsm.services.FSMGrammarAccess;
import org.eclipse.etrice.core.fsm.validation.FSMValidationUtilXtend.Result;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.validation.Check;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
/**
* Custom validation rules.
*
* see http://www.eclipse.org/Xtext/documentation.html#validation
*/
public class FSMJavaValidator extends org.eclipse.etrice.core.fsm.validation.AbstractFSMJavaValidator {
public static final String PLAIN_STRING_DETAILCODE = "RoomJavaValidator.PlainStringDetailCode";
@Inject
private FSMValidationUtil validationUtil;
@Inject
FSMGrammarAccess grammar;
@Inject
BaseConverterService converterService;
@Check
public void checkRefinedStateUnique(RefinedState rs) {
StateGraph sg = (StateGraph) rs.eContainer();
TreeIterator<EObject> it = sg.eAllContents();
while (it.hasNext()) {
EObject obj = it.next();
if (obj!=rs && obj instanceof RefinedState)
if (rs.getTarget()==((RefinedState)obj).getTarget()) {
if (rs.eContainer().eContainer() instanceof ModelComponent)
error("refined state conflicts with nested refined state with same target", FSMPackage.Literals.REFINED_STATE__TARGET);
else
error("refined state not unique", FSMPackage.Literals.REFINED_STATE__TARGET);
}
}
}
@Check
public void checkRefinedStateNotEmpty(RefinedState rs) {
if (validationUtil.isRefinedStateEmpty(rs)) {
error("Refined state must not be empty (needs to have at least one action code or a subgraph).", FSMPackage.Literals.STATE__ENTRY_CODE);
}
}
@Check
public void checkStateNameUnique(SimpleState s) {
// Result result = validationUtil.isUniqueName(s, s.getName());
// if (!result.isOk())
// error(result.getMsg(), FSMPackage.Literals.SIMPLE_STATE__NAME);
}
@Check
public void checkTrPoint(TrPoint tp) {
Result result = validationUtil.isValid(tp);
if (!result.isOk())
error(result);
}
@Check
public void checkChoicePoint(ChoicePoint cp) {
// if (!validationUtil.isUniqueName(cp, cp.getName()).isOk())
// error("name is not unique", FSMPackage.Literals.CHOICE_POINT__NAME);
}
@Check
public void checkTransition(Transition trans) {
Result result = validationUtil.checkTransition(trans);
if (!result.isOk())
error(result);
if (trans instanceof InitialTransition) {
result = validationUtil.isConnectable(null, trans.getTo(), trans, (StateGraph)trans.eContainer());
}
else {
result = validationUtil.isConnectable(((NonInitialTransition)trans).getFrom(), trans.getTo(), trans, (StateGraph)trans.eContainer());
}
if (!result.isOk())
error(result);
}
@Check
public void checkState(org.eclipse.etrice.core.fsm.fSM.State state) {
Result result = validationUtil.checkState(state);
if (!result.isOk())
error(result);
ArrayList<Result> res = validationUtil.uniqueOriginTriggers(state);
for (Result r : res) {
error(r);
}
}
@Check
public void checkRefinedTransition(RefinedTransition rt) {
if (!(rt.eContainer().eContainer() instanceof ModelComponent)) {
StateGraph sg = (StateGraph) rt.eContainer();
int idx = sg.getRefinedTransitions().indexOf(rt);
error("RefinedTransition only allowed in top level state graph of an actor", sg, FSMPackage.Literals.STATE_GRAPH__REFINED_TRANSITIONS, idx);
}
}
@Check
public void checkDetailCode(DetailCode dc) {
if (dc.getLines().isEmpty()) {
error("detail code must not be empty", dc, FSMPackage.Literals.DETAIL_CODE__LINES);
return;
}
// ccstring is new standard for detail code
boolean isPlainStyle = false;
List<INode> lineNodes = NodeModelUtils.findNodesForFeature(dc, FSMPackage.Literals.DETAIL_CODE__LINES);
for(INode lineNode : lineNodes){
if(lineNode.getGrammarElement() instanceof RuleCall){
AbstractRule rule = ((RuleCall)lineNode.getGrammarElement()).getRule();
if(rule == grammar.getCC_STRINGRule()) {
if(!new CCStringIndentation(converterService.getCC_StringConverter().stripDelim(lineNode.getText())).hasConsistentIndentation())
warning("Inconsistent indentation", dc, FSMPackage.Literals.DETAIL_CODE__LINES, lineNodes.indexOf(lineNode));
} else if(rule == grammar.getSTRINGRule()) {
isPlainStyle = true;
if(Strings.countLineBreaks(lineNode.getText()) > 0)
warning("multi line string, use smart string instead", dc, FSMPackage.Literals.DETAIL_CODE__LINES, lineNodes.indexOf(lineNode));
}
}
}
if(isPlainStyle) {
// TODO quickfix here does not work yet, see FSMQuickfixProvider
warning("old style line string", dc, null, PLAIN_STRING_DETAILCODE);
}
}
@Check
public void checkUniqueNamesInStateGraph(final StateGraph sg) {
Multimap<String, StateGraphItem> names2items = ArrayListMultimap.create();
// fill the multimap with all objects
StateGraph stateGraph = sg;
do {
for (org.eclipse.etrice.core.fsm.fSM.State st : stateGraph.getStates()) {
// the parent state of refined states is in this scope - so we don't add the name now
if (!(st instanceof RefinedState)) {
names2items.put(st.getName(), st);
}
}
for (TrPoint tp : stateGraph.getTrPoints()) {
names2items.put(tp.getName(), tp);
}
for (ChoicePoint cp : stateGraph.getChPoints()) {
names2items.put(cp.getName(), cp);
}
for (Transition tr : stateGraph.getTransitions()) {
names2items.put(tr.getName(), tr);
}
if (stateGraph.eContainer() instanceof RefinedState) {
stateGraph = ((RefinedState)stateGraph.eContainer()).getTarget().getSubgraph();
}
else if (sg.eContainer() instanceof ModelComponent) {
ModelComponent base = ((ModelComponent)stateGraph.eContainer()).getBase();
stateGraph = base!=null? base.getStateMachine():null;
}
else {
break;
}
}
while (stateGraph != null);
// check for duplicates
for (String key: names2items.keySet()) {
Collection<StateGraphItem> list = names2items.get(key);
if (list.size()>1) {
for (StateGraphItem item: list) {
if(sg.eContents().contains(item))
error("Name is not unique in state graph (including super graph)", item, getNameFeature(item));
else if(sg.eResource() == item.eResource())
warning("Name is also used in derived graph(s)", item, getNameFeature(item));
}
}
}
}
/**
* @param item
* @return
*/
private EStructuralFeature getNameFeature(StateGraphItem item) {
if (item instanceof SimpleState) {
return FSMPackage.Literals.SIMPLE_STATE__NAME;
}
else if (item instanceof ChoicePoint) {
return FSMPackage.Literals.CHOICE_POINT__NAME;
}
else if (item instanceof TrPoint) {
return FSMPackage.Literals.TR_POINT__NAME;
}
else if (item instanceof Transition) {
return FSMPackage.Literals.TRANSITION__NAME;
}
else {
assert(false): "internal error: unexpected sub type";
return null;
}
}
private void error(Result result) {
error(result.getMsg(), result.getSource(), result.getFeature(), result.getIndex());
}
}