/** * 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.ActionHelper; import org.sintef.thingml.helpers.AnnotatedElementHelper; import org.sintef.thingml.helpers.ConfigurationHelper; import org.thingml.compilers.Context; import org.thingml.compilers.configuration.CfgMainGenerator; import java.util.HashSet; import java.util.Set; /** * Created by bmori on 10.12.2014. */ public class PlantUMLCfgMainGenerator extends CfgMainGenerator { private Set<String> classes = new HashSet<>(); private Set<String> includes = new HashSet<>(); private void generateIncludes(Thing thing, StringBuilder classes) { for (Thing include : thing.getIncludes()) { if(!includes.contains(include.getName() + "<" + thing.getName())) { classes.append(include.getName()); classes.append(" <|-- "); classes.append(thing.getName()); classes.append("\n"); includes.add(include.getName() + "<" + thing.getName()); } generateIncludes(include, classes); } } private boolean isPSM(Thing thing) { return ThingMLHelpers.getAllExpressions(thing, ExternExpression.class).size() > 0 || ActionHelper.getAllActions(thing, ExternStatement.class).size() > 0; } private boolean isGUI(Thing thing) { return AnnotatedElementHelper.isDefined(thing,"mock", "true"); } private void generateClass(Thing thing, StringBuilder classes, Context ctx, boolean compact) { if (!this.classes.contains(thing.getName())) { this.classes.add(thing.getName()); if (thing.isFragment()) { classes.append("class " + thing.getName() + " <<(F,#BC74ED)Fragment>> {\n"); } else if (isGUI(thing)) { classes.append("class " + thing.getName() + " <<(G,#5394FC)GUI>> {\n"); } else if(isPSM(thing)) {//PSM classes.append("class " + thing.getName() + " <<(T,#F94918)PSM>> {\n"); } else {//PIM classes.append("class " + thing.getName() + " <<(T,#5BBF09)PIM>> {\n"); } if (thing.getProperties().size() > 0) classes.append("..Properties..\n"); for (Property p : thing.getProperties()) { classes.append("-" + p.getName() + " : " + p.getType().getName()); if (p.getInit() != null) { classes.append(" = "); ctx.getCompiler().getThingActionCompiler().generate(p.getInit(), classes, ctx); } classes.append("\n"); } if (thing.getMessages().size() > 0) classes.append("..Messages..\n"); for (Message m : thing.getMessages()) { classes.append("-" + m.getName() + "("); if (compact) { if (m.getParameters().size() > 0) classes.append("..."); } else { int i = 0; for (Parameter p : m.getParameters()) { if (i > 0) classes.append(", "); classes.append(p.getName() + " : " + p.getType().getName()); } } classes.append(")"); classes.append("\n"); } for (Port p : thing.getPorts()) { classes.append("..Port " + p.getName() + "..\n"); for (Message m : p.getReceives()) { classes.append(">>" + m.getName() + "\n"); } for (Message m : p.getSends()) { classes.append("<<" + m.getName() + "\n"); } } if (thing.getFunctions().size() > 0) classes.append("..Functions..\n"); for (Function f : thing.getFunctions()) { classes.append("-" + f.getName() + "("); if (compact) { if (f.getParameters().size() > 0) classes.append("..."); } else { int i = 0; for (Parameter p : f.getParameters()) { if (i > 0) classes.append(", "); classes.append(p.getName() + " : " + p.getType().getName()); } } classes.append(") : "); if (f.getType() != null) { classes.append(f.getType().getName()); } else { classes.append("void"); } classes.append("\n"); } classes.append("}\n"); if (thing.getAnnotations().size() > 0) classes.append("note left of " + thing.getName() + " : "); for(PlatformAnnotation a : thing.getAnnotations()) { classes.append("<b>@" + a.getName() + "</b> <color:royalBlue>\"" + a.getValue().replace("\n", "\\n") + "\"</color>\\n"); } if (thing.getAnnotations().size() > 0) classes.append("\n"); for (Thing include : thing.getIncludes()) { generateClass(include, classes, ctx, compact); } } } @Override public void generateMainAndInit(Configuration cfg, ThingMLModel model, Context ctx) { //Instance component diagram final StringBuilder builder = ctx.getBuilder(cfg.getName() + "/docs/" + cfg.getName() + ".plantuml"); builder.append("@startuml\n"); builder.append("caption Instances and Connectors in configuration " + cfg.getName() + "\n"); for (Instance i : ConfigurationHelper.allInstances(cfg)) { builder.append("component " + i.getName() + (isGUI(i.getType())?"<<GUI>>":isPSM(i.getType())?"<<PSM>>":"<<PIM>>") + "\n"); } for(Protocol p : ConfigurationHelper.getUsedProtocols(cfg)) { builder.append("boundary " + p.getName() + "\n"); } for (Connector c : ConfigurationHelper.allConnectors(cfg)) { builder.append(c.getCli().getInstance().getName() + " -(0- " + c.getSrv().getInstance().getName() + " : " + c.getRequired().getName() + " => " + c.getProvided().getName() + "\n"); } for (ExternalConnector c : ConfigurationHelper.getExternalConnectors(cfg)) { builder.append(c.getInst().getInstance().getName() + " .. " + c.getProtocol().getName() + " : " + c.getPort().getName() + "\n"); } builder.append("@enduml"); //Type/Thing class diagram final StringBuilder classes = ctx.getBuilder(cfg.getName() + "/docs/" + cfg.getName() + "_class.plantuml"); final StringBuilder classes2 = ctx.getBuilder(cfg.getName() + "/docs/" + cfg.getName() + "_class_compact.plantuml"); classes.append("@startuml\n"); classes.append("caption Things used in configuration " + cfg.getName() + "\n"); classes2.append("@startuml\n"); classes2.append("caption Things used in configuration " + cfg.getName() + "\n"); this.classes.clear(); for(Thing thing : ConfigurationHelper.allThings(cfg)) { generateClass(thing, classes, ctx, false); } this.classes.clear(); for(Thing thing : ConfigurationHelper.allThings(cfg)) { generateClass(thing, classes2, ctx, true); } this.includes.clear(); for(Thing thing : ConfigurationHelper.allThings(cfg)) { generateIncludes(thing, classes); } this.includes.clear(); for(Thing thing : ConfigurationHelper.allThings(cfg)) { generateIncludes(thing, classes2); } classes.append("@enduml"); classes2.append("@enduml"); final StringBuilder datatypes = ctx.getBuilder(cfg.getName() + "/docs/" + cfg.getName() + "_datatypes.plantuml"); datatypes.append("@startuml\n"); datatypes.append("caption Datatypes used in configuration " + cfg.getName() + "\n"); for(Type t : ThingMLHelpers.allUsedSimpleTypes(ThingMLHelpers.findContainingModel(cfg))) { if (t instanceof PrimitiveType) { datatypes.append("class " + t.getName() + " <<(D,#D2E524)" + ((PrimitiveType) t).getByteSize() + ">> {\n"); } else if (t instanceof ObjectType){ datatypes.append("class " + t.getName() + " <<(O,#E5D224)>> {\n"); } else if (t instanceof Enumeration) { datatypes.append("class " + t.getName() + " <<(E,#24E5B2)>> {\n"); final Enumeration e = (Enumeration)t; for(EnumerationLiteral l : e.getLiterals()) { datatypes.append("-" + l.getName() + "\n"); } } datatypes.append("}\n"); if (t.getAnnotations().size() > 0) datatypes.append("note bottom of " + t.getName() + " : "); for(PlatformAnnotation a : t.getAnnotations()) { datatypes.append("<b>@" + a.getName() + "</b> <color:royalBlue>\"" + a.getValue().replace("\n", "\\n") + "\"</color>\\n"); } if (t.getAnnotations().size() > 0) datatypes.append("\n"); } datatypes.append("@enduml"); //TODO: Protocols } }