Commit 77bbfd5a authored by Eyrak Paen-Rochlitz's avatar Eyrak Paen-Rochlitz
Browse files

[generator.doc] Only include diagrams if model files can be resolved

parent f40db328
......@@ -19,8 +19,6 @@ import com.google.inject.Singleton
import java.util.List
import org.eclipse.emf.ecore.EObject
import org.eclipse.etrice.core.common.base.Documentation
import org.eclipse.etrice.core.fsm.fSM.ModelComponent
import org.eclipse.etrice.core.fsm.fSM.State
import org.eclipse.etrice.core.fsm.fSM.StateGraph
import org.eclipse.etrice.core.genmodel.etricegen.Root
import org.eclipse.etrice.core.room.ActorClass
......@@ -36,13 +34,13 @@ import org.eclipse.etrice.core.room.RoomModel
import org.eclipse.etrice.core.room.SubSystemClass
import org.eclipse.etrice.core.room.util.RoomHelpers
import org.eclipse.etrice.generator.base.io.IGeneratorFileIO
import org.eclipse.etrice.generator.fsm.base.CodegenHelpers
import org.eclipse.etrice.generator.doc.gen.URIBasedDiagramResolver.DiagramType
import org.eclipse.etrice.generator.doc.gen.URIBasedDiagramResolver.DiagramFormat
import org.eclipse.xtext.documentation.IEObjectDocumentationProvider
import static org.eclipse.etrice.core.common.documentation.DocumentationMarkup.*
import org.eclipse.etrice.generator.doc.Main
import org.eclipse.etrice.generator.base.AbstractGeneratorOptionsHelper
import org.eclipse.etrice.core.room.StructureClass
import static extension org.eclipse.xtext.EcoreUtil2.getContainerOfType
......@@ -50,9 +48,9 @@ import static extension org.eclipse.xtext.EcoreUtil2.getContainerOfType
class AsciiDocGen {
@Inject extension RoomHelpers
@Inject extension CodegenHelpers
@Inject protected extension AbstractGeneratorOptionsHelper
@Inject IEObjectDocumentationProvider eObjDocuProvider
@Inject extension URIBasedDiagramResolver
def doGenerate(Root root, IGeneratorFileIO fileIO, boolean includeImages) {
val packages = root.models.groupBy[name].entrySet.map[new RoomPackage(key, value)].sortBy[name]
......@@ -206,9 +204,9 @@ class AsciiDocGen {
tagStart(ssc)
ssc.docText
IF includeImages
IF includeImages && ssc.hasDiagram(DiagramType.STRUCTURE)
includeImage(ssc.diagramName + ".jpg")
includeImage(ssc.getExportedDiagramFilename(DiagramType.STRUCTURE, DiagramFormat.JPG).orElse("!!unresolved_diagram_file!!"))
ENDIF
tagEnd(ssc)
'''
......@@ -320,9 +318,8 @@ class AsciiDocGen {
ENDIF
ELSE
==== Structure
IF includeImages
includeImage(ac.diagramName + ".jpg")
IF includeImages && ac.hasDiagram(DiagramType.STRUCTURE)
includeImage(ac.getExportedDiagramFilename(DiagramType.STRUCTURE, DiagramFormat.JPG).orElse("!!unresolved_diagram_file!!"))
ENDIF
IF !ac.allPorts.empty
......@@ -360,8 +357,8 @@ class AsciiDocGen {
def private CharSequence generateStateGraphDoc(ActorClass ac, StateGraph stateGraph, boolean includeImages, int depth) {
'''
IF includeImages
includeImage(stateGraph.diagramName + ".jpg")
IF includeImages && stateGraph.hasDiagram(DiagramType.BEHAVIOR)
includeImage(stateGraph.getExportedDiagramFilename(DiagramType.BEHAVIOR, DiagramFormat.JPG).orElse("!!unresolved_diagram_file!!"))
ENDIF
FOR state: stateGraph.states
state.name::fill(':', depth)
......@@ -467,18 +464,6 @@ class AsciiDocGen {
if(index != -1) docText.subSequence(0, index) else docText
}
def private dispatch getDiagramName(StructureClass sc) {
val namespace = sc.getContainerOfType(RoomModel)?.name?.concat(".") ?: ""
namespace + sc.name + "_structure"
}
def private dispatch getDiagramName(StateGraph sg) {
val namespace = sg.getContainerOfType(RoomModel)?.name?.concat(".") ?: ""
val modelComponentName = sg.getContainerOfType(ModelComponent)?.componentName ?: ""
val subStatePath = if (sg.eContainer instanceof State) "." + (sg.eContainer as State)?.genStatePathName else ""
namespace + modelComponentName + subStatePath + "_behavior"
}
def private getInstanceDiagramName(LogicalSystem system) {
val namespace = system.getContainerOfType(RoomModel)?.name?.concat(".") ?: ""
namespace + system.name + "_instanceTree"
......
/*******************************************************************************
* Copyright (c) 2022 protos software gmbh (http://www.protos.de).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* CONTRIBUTORS:
* epaen (initial contribution)
*
*******************************************************************************/
package org.eclipse.etrice.generator.doc.gen;
import java.io.File;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.etrice.core.fsm.fSM.ModelComponent;
import org.eclipse.etrice.core.fsm.fSM.State;
import org.eclipse.etrice.core.fsm.fSM.util.FSMSwitch;
import org.eclipse.etrice.core.fsm.naming.FSMNameProvider;
import org.eclipse.etrice.core.room.StructureClass;
import org.eclipse.etrice.core.room.util.RoomSwitch;
import org.eclipse.xtext.EcoreUtil2;
import java.util.Optional;
import com.google.inject.Inject;
public class URIBasedDiagramResolver {
@Inject
private BehaviorBaseNameProvider behaviorBaseProvider;
@Inject
private StructureBaseNameProvider structureBaseProvider;
public static final String DIAGRAM_SUBDIR = "diagrams";
public static enum DiagramType {
STRUCTURE("_structure", ".structure"),
BEHAVIOR("_behavior", ".behavior");
private final String exportSuffix;
private final String extension;
DiagramType(String exportSuffix, String extension) {
this.exportSuffix = exportSuffix;
this.extension = extension;
}
public String getExportSuffix() {
return exportSuffix;
}
public String getExtension() {
return extension;
}
}
public static enum DiagramFormat {
JPG("jpg"),
PNG("png"),
SVG("svg"),
NO_EXT("");
private final String extension;
DiagramFormat(String extension) {
this.extension = extension;
}
public String getExtension() {
return extension;
}
}
/**
* Checks if the corresponding diagram file for a given EObject instance exists.
*
* @param obj The EObject associated with the diagram
* @param type The desired diagram type to check
* @return true if the diagram file exists, or false if none could be found
*/
public boolean hasDiagram(EObject obj, DiagramType type) {
Optional<URI> uri = getDiagramURI(obj, type);
if (!uri.isPresent()) {
return false;
}
boolean result = false;
if (uri.get().isPlatformResource()) {
String path = uri.get().toPlatformString(true);
IResource file = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
result = (file != null) && (file.exists());
} else if (uri.get().isFile()) {
String path = uri.get().toFileString();
File file = new File(path);
result = file.exists();
}
return result;
}
/**
* Gets the corresponding exported diagram image filename for a given EObject.
*
* @param obj The EObject associated with the diagram
* @param type The diagram type of the exported diagram
* @param format The format of the exported diagram
* @return true if the diagram file exists, or false if not, or false if
* the EObject could not be mapped to a diagram file
*/
public Optional<String> getExportedDiagramFilename(EObject obj, DiagramType type, DiagramFormat format) {
Optional<String> base = Optional.empty();
switch (type) {
case BEHAVIOR:
base = behaviorBaseProvider.doSwitch(obj);
break;
case STRUCTURE:
base = structureBaseProvider.doSwitch(obj);
break;
default:
break;
}
return base.map(value -> value + type.getExportSuffix() + "." + format.getExtension());
}
/**
* Gets the corresponding diagram filename for a given EObject.
*
* @param obj The EObject associated with the diagram
* @param type The desired diagram type
* @return The filename for the diagram of the EObject, or empty if none could be found
*/
public Optional<String> getDiagramFilename(EObject obj, DiagramType type) {
Optional<String> base = Optional.empty();
switch (type) {
case BEHAVIOR:
// Restrict to ModelComponent, since State diagrams are nested in the same file
ModelComponent mc = EcoreUtil2.getContainerOfType(obj, ModelComponent.class);
base = behaviorBaseProvider.doSwitch(mc);
break;
case STRUCTURE:
base = structureBaseProvider.doSwitch(obj);
break;
default:
break;
}
return base.map(value -> value + type.getExtension());
}
/**
* Gets the corresponding URI of the diagram file for a given EObject.
*
* @param obj The EObject associated with the diagram
* @param type The diagram type of the exported diagram
* @return The URI for the diagram of the EObject, or empty if none could be found
*/
public Optional<URI> getDiagramURI(EObject obj, DiagramType type) {
URI diagramFolder = resolveDiagramFolder(obj);
Optional<String> filename = getDiagramFilename(obj, type);
return filename.map(value -> diagramFolder.appendSegment(value));
}
private URI resolveDiagramFolder(EObject obj) {
URI uri = obj.eResource().getURI();
URI uriParent = uri.trimSegments(1);
URI uriDiagramContainer = uriParent.appendSegment(DIAGRAM_SUBDIR);
return uriDiagramContainer;
}
/**
* Derives a base name for behavior diagrams based on a given EObject.
* The format of the base name is "[namespace.]structureClassName"
*
* <p>The provider will look for the closest containing StructureClass of
* the given EObject.</p>
*/
private static class StructureBaseNameProvider extends RoomSwitch<Optional<String>> {
@Override
public Optional<String> caseStructureClass(StructureClass object) {
if (object == null) {
return Optional.empty();
}
String namespace = URIBasedDiagramResolver.inferNamespaceName(object);
if (!namespace.isEmpty()) {
namespace += ".";
}
return Optional.of(namespace + object.getName());
}
@Override
public Optional<String> defaultCase(EObject object) {
StructureClass sc = EcoreUtil2.getContainerOfType(object, StructureClass.class);
return caseStructureClass(sc);
}
}
/**
* Derives a base name for behavior diagrams based on a given EObject.
*
* <ul>
* <li>for States this looks like "[namespace.]componentName.statePath"</li>
* <li>for ModelComponents this looks like "[namespace.]componentName"</li>
* </ul>
*
* <p>The provider will look for the closest containing State or ModelComponent
* of the given EObject, in that order.</p>
*/
private static class BehaviorBaseNameProvider extends FSMSwitch<Optional<String>> {
@Inject
FSMNameProvider nameProvider;
@Override
public Optional<String> caseModelComponent(ModelComponent object) {
if (object == null) {
return Optional.empty();
}
String namespace = URIBasedDiagramResolver.inferNamespaceName(object);
if (!namespace.isEmpty()) {
namespace += ".";
}
return Optional.of(namespace + object.getComponentName());
}
@Override
public Optional<String> caseState(State object) {
if (object == null) {
return Optional.empty();
}
ModelComponent mc = EcoreUtil2.getContainerOfType(object, ModelComponent.class);
return caseModelComponent(mc).map(value -> value + "." + nameProvider.getStatePathName(object));
}
@Override
public Optional<String> defaultCase(EObject object) {
State st = EcoreUtil2.getContainerOfType(object, State.class);
if (st != null) {
return caseState(st);
}
ModelComponent mc = EcoreUtil2.getContainerOfType(object, ModelComponent.class);
if (mc!= null) {
return caseModelComponent(mc);
}
// fallback if no match
return Optional.empty();
}
}
/**
* Looks for name attribute in a given EObject's container to use as a namespace name.
* This is done reflexively by searching for an EAttribute "name", because in the base
* FSM model there may not be a named root element to derive the namespace from.
*
* @param obj The EObject used to find the parent namespace name
* @return A namespace name as String, or empty String if none is found.
*/
private static String inferNamespaceName(EObject obj) {
EObject container = obj.eContainer();
if (container == null) {
return "";
}
EAttribute namespaceAttr = container.eClass().getEAllAttributes().stream()
.filter(attr -> attr.getName().equals("name"))
.findFirst()
.orElse(null);
if (namespaceAttr == null) {
return "";
}
String name = container.eGet(namespaceAttr).toString();
return name;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment