/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. */ package org.thingml.compilers.uml; import org.sintef.thingml.*; import org.sintef.thingml.constraints.ThingMLHelpers; import org.sintef.thingml.helpers.CompositeStateHelper; import org.sintef.thingml.helpers.StateHelper; import org.thingml.compilers.Context; import org.thingml.compilers.thing.common.FSMBasedThingImplCompiler; /** * Created by bmori on 16.04.2015. */ public class PlantUMLThingImplCompiler extends FSMBasedThingImplCompiler { public static boolean FACTORIZE_TRANSITIONS = true; private void doBuildAction(Action a, StringBuilder builder, Context ctx) { ctx.getCompiler().getThingActionCompiler().generate(a, builder, ctx); } private void buildActions(State s, StringBuilder builder, Context ctx) { if (s.getEntry() != null) {//TODO: pretty-print ThingML actions and expressions builder.append("\t" + s.getName() + " : entry / "); doBuildAction(s.getEntry(), builder, ctx); builder.append("\n"); } if (s.getExit() != null) { builder.append("\t" + s.getName() + " : exit / "); doBuildAction(s.getExit(), builder, ctx); builder.append("\n"); } } private void doGenerateHandlers(Handler h, StringBuilder builder, Context ctx) { if (h.getEvent().size() == 0) { generateHandler(h, null, null, builder, ctx); } else { for (Event e : h.getEvent()) { ReceiveMessage rm = (ReceiveMessage) e; generateHandler(h, rm.getMessage(), rm.getPort(), builder, ctx); } } } private void generateHandlers(State c, StringBuilder builder, Context ctx) { for (Handler h : c.getOutgoing()) { doGenerateHandlers(h, builder, ctx); } for (Handler h : c.getInternal()) { doGenerateHandlers(h, builder, ctx); } } protected void generateStateMachine(StateMachine sm, StringBuilder builder, Context ctx) { builder.append("@startuml\n"); builder.append("skinparam defaultTextAlignment left\n"); builder.append("caption Behavior of thing " + ThingMLHelpers.findContainingThing(sm).getName() + "\n"); builder.append("[*] --> " + sm.getName() + "\n"); builder.append("state " + sm.getName() + "{\n"); for (State s : sm.getSubstate()) { if (!(s instanceof Session)) generateState(s, builder, ctx); } builder.append("[*] --> " + sm.getInitial().getName() + "\n"); buildActions(sm, builder, ctx); generateHandlers(sm, builder, ctx); for (Region r : sm.getRegion()) { generateRegion(r, builder, ctx); } for(Session s : CompositeStateHelper.allFirstLevelSessions(sm)) { generateRegion(s, builder, ctx); } builder.append("}\n"); builder.append("@enduml\n"); } protected void generateCompositeState(CompositeState c, StringBuilder builder, Context ctx) { builder.append("state " + c.getName() + "{\n"); for (State s : c.getSubstate()) { if (!(s instanceof Session)) generateState(s, builder, ctx); } builder.append("[*] --> " + c.getInitial().getName() + "\n"); generateHandlers(c, builder, ctx); for (Region r : c.getRegion()) { generateRegion(r, builder, ctx); } for(Session s : CompositeStateHelper.allFirstLevelSessions(c)) { generateRegion(s, builder, ctx); } buildActions(c, builder, ctx); builder.append("}\n"); } protected void generateAtomicState(State s, StringBuilder builder, Context ctx) { builder.append("state " + s.getName() + "{\n"); buildActions(s, builder, ctx); generateHandlers(s, builder, ctx); builder.append("}\n"); } protected void generateFinalState(FinalState s, StringBuilder builder, Context ctx) { generateAtomicState(s, builder, ctx); builder.append(s.getName() + " --> [*]\n"); } public void generateRegion(Region r, StringBuilder builder, Context ctx) { builder.append("--\n"); builder.append("[*] --> " + r.getInitial().getName() + "\n"); for (State s : r.getSubstate()) { generateState(s, builder, ctx); } if (r instanceof Session) { builder.append("Note top of " + r.getInitial().getName() + " : Session " + r.getName() + "\n"); } else { builder.append("Note top of " + r.getInitial().getName() + " : Region " + r.getName() + "\n"); } } private void generateGuardAndActions(Handler t, StringBuilder builder, Context ctx) { if (t.getGuard() != null) { builder.append("["); ctx.getCompiler().getThingActionCompiler().generate(t.getGuard(), builder, ctx); builder.append("]"); } if (t.getAction() != null) { if (t.getEvent().size() == 0 && t.getGuard() == null) builder.append("action "); else builder.append("\\naction "); ctx.getCompiler().getThingActionCompiler().generate(t.getAction(), builder, ctx); } } protected void generateTransition(Transition t, Message msg, Port p, StringBuilder builder, Context ctx) { String content = builder.toString(); String transition = t.getSource().getName() + " --> " + t.getTarget().getName(); if ((msg != null && p != null) || t.getAction() != null || t.getGuard() != null) { transition = transition + " : "; } if (FACTORIZE_TRANSITIONS && content.contains(transition)) {//FIXME: not the most elegant way to manage this factorization... StringBuilder temp = new StringBuilder(); temp.append(transition); if (msg != null && p != null) { if(t.getEvent().size() > 0 && t.getEvent().get(0).getName()!= null) temp.append(t.getEvent().get(0).getName() + ":"); temp.append(p.getName() + "?" + msg.getName()); } generateGuardAndActions(t, temp, ctx); content = content.replace(transition, "\n" + temp.toString() + "\\n||"); builder.delete(0, builder.length()); builder.append(content); } else { builder.append("\n" + t.getSource().getName() + " --> " + t.getTarget().getName()); if ((msg != null && p != null) || t.getAction() != null || t.getGuard() != null) { builder.append(" : "); } if (msg != null && p != null) { if(t.getEvent().size() > 0 && t.getEvent().get(0).getName()!= null) builder.append(t.getEvent().get(0).getName() + ":"); builder.append(p.getName() + "?" + msg.getName()); } generateGuardAndActions(t, builder, ctx); builder.append("\n"); } } protected void generateInternalTransition(InternalTransition t, Message msg, Port p, StringBuilder builder, Context ctx) { builder.append("\t" + ThingMLHelpers.findContainingState(t).getName() + " : "); if(t.getEvent().size() > 0 && t.getEvent().get(0).getName()!= null) builder.append(t.getEvent().get(0).getName() + ":"); if(p != null && msg != null) builder.append(p.getName() + "?" + msg.getName() + " / "); generateGuardAndActions(t, builder, ctx); builder.append("\n"); } }