Skip to content

Commit db8e418

Browse files
committed
Control program exporter for LTSs refined
1 parent 30169ab commit db8e418

File tree

8 files changed

+198
-19
lines changed

8 files changed

+198
-19
lines changed

src/main/java/nl/utwente/groove/explore/util/LTSReporter.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,26 @@ static public File exportLTS(GTS lts, String filePattern, LTSLabels labels, Filt
9696
File outFile = new File(dir, ltsName);
9797
var fileType = FileType.getType(outFile);
9898
var exporter = Exporters.getExporter(ExportKind.GRAPH, fileType);
99-
var exportable = Exportable.graph(ltsGraph);
100-
if (exporter != null && exporter.exports(exportable)) {
101-
try {
102-
exporter.doExport(exportable, outFile, fileType);
103-
} catch (PortException e1) {
104-
throw new IOException(e1);
105-
}
106-
} else {
99+
if (exporter == null) {
107100
if (!FileType.hasAnyExtension(outFile)) {
108101
outFile = FileType.GXL.addExtension(outFile);
109102
}
110103
Groove.saveGraph(ltsGraph, outFile);
104+
} else {
105+
var exportable = Exportable.graph(ltsGraph);
106+
if (!exporter.exports(exportable)) {
107+
exportable = Exportable.graph(gtsFragment);
108+
}
109+
if (exporter.exports(exportable)) {
110+
try {
111+
exporter.doExport(exportable, outFile, fileType);
112+
} catch (PortException e1) {
113+
throw new IOException(e1);
114+
}
115+
} else {
116+
throw new IOException("Exporter for %s refuses to process LTS graph");
117+
}
118+
111119
}
112120
return outFile;
113121
}

src/main/java/nl/utwente/groove/io/FileType.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,10 @@ static public String getExtension(File file) {
424424
* @return the extension part of {@code filename}
425425
*/
426426
static public String getExtension(String filename) {
427-
return filename.substring(filename.lastIndexOf(SEPARATOR));
427+
int sepIndex = filename.lastIndexOf(SEPARATOR);
428+
return sepIndex < 0
429+
? ""
430+
: filename.substring(sepIndex);
428431
}
429432

430433
/** Tests if a given file has a pure name part,

src/main/java/nl/utwente/groove/io/external/AbstractExporter.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public final ExportKind getExportKind() {
4343
/** The export kind of this exporter. */
4444
private final ExportKind exportKind;
4545

46+
@Override
47+
public boolean exports(Exportable exportable) {
48+
return exportable.hasExportKind(getExportKind());
49+
}
50+
4651
@Override
4752
public Set<FileType> getFileTypes() {
4853
return this.fileTypes;
@@ -64,7 +69,7 @@ protected final void register(FileType fileType) {
6469
*/
6570
@Override
6671
public Set<FileType> getFileTypes(Exportable exportable) {
67-
if (exportable.hasExportKind(getExportKind())) {
72+
if (exports(exportable)) {
6873
return getFileTypes();
6974
} else {
7075
return Collections.emptySet();
@@ -130,9 +135,25 @@ protected abstract void initialise(Exportable exportable,
130135

131136
/** Writes a line to the export file. */
132137
public void emit(String line) {
133-
this.writer.println(line);
138+
this.writer.println(this.indent + line);
139+
}
140+
141+
/** Adds an step to the space indentation prefixed to every {@link #emit(String)} line. */
142+
public void increaseIndent() {
143+
this.indent.append(INDENT_STEP);
134144
}
135145

146+
/** Removes a step from the space indentation prefixed to every {@link #emit(String)} line. */
147+
public void decreaseIndent() {
148+
this.indent.delete(0, INDENT_STEP.length());
149+
}
150+
151+
/** Indentation prefixed to every {@link #emit(String)} line. */
152+
private final StringBuffer indent = new StringBuffer("");
153+
154+
/** Increase to the indent upon invocation of {@link #increaseIndent()}. */
155+
static private final String INDENT_STEP = " ";
156+
136157
private PrintWriter writer;
137158
}
138159
}

src/main/java/nl/utwente/groove/io/external/Exporter.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,12 @@ default public boolean hasExportKind(ExportKind exportKind) {
4747

4848
/**
4949
* Indicates if this exporter is suitable for processing a given exportable.
50-
* This is true if and only if {@link #getFileTypes(Exportable)} is non-empty.
5150
*/
52-
default public boolean exports(Exportable exportable) {
53-
return !getFileTypes(exportable).isEmpty();
54-
}
51+
public boolean exports(Exportable exportable);
5552

56-
/** Returns the file types that can be used for a given exportable. */
53+
/** Returns the file types that can be used for a given exportable.
54+
* This is nonempty if and only if {@link #exports(Exportable)} holds.
55+
*/
5756
public Set<FileType> getFileTypes(Exportable exportable);
5857

5958
/**

src/main/java/nl/utwente/groove/io/external/Exporters.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import nl.utwente.groove.io.external.format.EcorePorter;
3939
import nl.utwente.groove.io.external.format.FsmExporter;
4040
import nl.utwente.groove.io.external.format.GraphExportListener.DotListener;
41+
import nl.utwente.groove.io.external.format.LTS2ControlExporter;
4142
import nl.utwente.groove.io.external.format.ListenerExporter;
4243
import nl.utwente.groove.io.external.format.NativePorter;
4344
import nl.utwente.groove.io.external.format.RasterExporter;
@@ -136,6 +137,7 @@ private static List<Exporter> createExporters() {
136137
result.add(TikzExporter.getInstance());
137138
result.add(EcorePorter.instance());
138139
result.add(ListenerExporter.instance(DotListener.instance()));
140+
result.add(LTS2ControlExporter.instance());
139141
return Collections.unmodifiableList(result);
140142
}
141143

src/main/java/nl/utwente/groove/io/external/format/AbstractResourcePorter.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
import nl.utwente.groove.grammar.model.GrammarModel;
3434
import nl.utwente.groove.grammar.model.ResourceKind;
3535
import nl.utwente.groove.grammar.model.TextBasedModel;
36+
import nl.utwente.groove.graph.GraphRole;
3637
import nl.utwente.groove.io.FileType;
3738
import nl.utwente.groove.io.external.AbstractExporter;
3839
import nl.utwente.groove.io.external.Exportable;
39-
import nl.utwente.groove.io.external.Exporter;
4040
import nl.utwente.groove.io.external.Imported;
4141
import nl.utwente.groove.io.external.Importer;
4242
import nl.utwente.groove.io.external.PortException;
@@ -52,7 +52,7 @@
5252
public class AbstractResourcePorter extends AbstractExporter implements Importer {
5353
/** Constructor for subclasses. */
5454
protected AbstractResourcePorter() {
55-
super(Exporter.ExportKind.RESOURCE);
55+
super(ExportKind.RESOURCE);
5656
this.fileTypeMap = new EnumMap<>(ResourceKind.class);
5757
this.resourceKindMap = new EnumMap<>(FileType.class);
5858
}
@@ -84,9 +84,21 @@ protected final void register(ResourceKind kind, FileType fileType) {
8484
/** Map from file type the native file type. */
8585
private final Map<FileType,ResourceKind> resourceKindMap;
8686

87+
@Override
88+
public boolean exports(Exportable exportable) {
89+
boolean result = super.exports(exportable);
90+
if (result) {
91+
var graph = exportable.graph();
92+
assert graph != null;
93+
var graphRole = graph.getRole();
94+
result = graphRole == GraphRole.HOST || graphRole == GraphRole.TYPE;
95+
}
96+
return result;
97+
}
98+
8799
@Override
88100
public Set<FileType> getFileTypes(Exportable exportable) {
89-
if (exportable.hasExportKind(getExportKind())) {
101+
if (exports(exportable)) {
90102
return Collections.singleton(getFileType(exportable.getResourceKind()));
91103
} else {
92104
return Collections.emptySet();
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* GROOVE: GRaphs for Object Oriented VErification
2+
* Copyright 2003--2023 University of Twente
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing,
10+
* software distributed under the License is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12+
* either express or implied. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* $Id$
16+
*/
17+
package nl.utwente.groove.io.external.format;
18+
19+
import java.util.HashSet;
20+
import java.util.Set;
21+
22+
import nl.utwente.groove.graph.EdgeRole;
23+
import nl.utwente.groove.io.FileType;
24+
import nl.utwente.groove.io.external.AbstractExporter;
25+
import nl.utwente.groove.io.external.Exportable;
26+
import nl.utwente.groove.io.external.PortException;
27+
import nl.utwente.groove.lts.GTS;
28+
import nl.utwente.groove.lts.GTSFragment;
29+
import nl.utwente.groove.lts.GraphState;
30+
31+
/**
32+
* Class that exports an LTS to a control program that enforces precisely the transitions in that LTS.
33+
* @author Arend Rensink
34+
* @version $Revision$
35+
*/
36+
public class LTS2ControlExporter extends AbstractExporter.Writer {
37+
/**
38+
* Constructor for the singleton instance.
39+
*/
40+
private LTS2ControlExporter() {
41+
super(ExportKind.GRAPH);
42+
register(FileType.CONTROL);
43+
}
44+
45+
@Override
46+
public boolean exports(Exportable exportable) {
47+
var graph = exportable.graph();
48+
return super.exports(exportable) && (graph instanceof GTS || graph instanceof GTSFragment);
49+
}
50+
51+
@Override
52+
protected void initialise(Exportable exportable, FileType fileType) throws PortException {
53+
var graph = exportable.graph();
54+
if (graph instanceof GTS gts) {
55+
this.gts = gts.toFragment(true, false);
56+
} else if (graph instanceof GTSFragment fragment) {
57+
this.gts = fragment;
58+
} else {
59+
throw new PortException("Cannot export %s to %s: not an LTS", exportable.qualName(),
60+
fileType);
61+
}
62+
}
63+
64+
private GTSFragment gts;
65+
66+
@Override
67+
protected void execute() throws PortException {
68+
this.covered.clear();
69+
var start = this.gts.startState();
70+
this.covered.add(start);
71+
emit(start);
72+
}
73+
74+
/** Recursively emits the properties that hold in this states, followed by the choice of outgoing transitions. */
75+
private void emit(GraphState state) {
76+
assert this.covered.contains(state);
77+
emit("// state " + state);
78+
state
79+
.getTransitions()
80+
.stream()
81+
.filter(t -> t.getRole() == EdgeRole.FLAG)
82+
.filter(t -> !t.label().getAction().getRole().isConstraint())
83+
.forEach(t -> emit(t.label() + ";"));
84+
var outs = state
85+
.getTransitions()
86+
.stream()
87+
.filter(t -> this.gts.edgeSet().contains(t))
88+
.filter(t -> this.covered.add(t.target()))
89+
.toList();
90+
if (outs.isEmpty()) {
91+
emit(state.isFinal()
92+
? "// final state"
93+
: "halt");
94+
} else if (outs.size() == 1 && !state.isFinal()) {
95+
var out = outs.get(0);
96+
emit(out.label() + ";");
97+
emit(out.target());
98+
} else {
99+
boolean first = true;
100+
for (var out : outs) {
101+
if (first) {
102+
emit("choice {");
103+
first = false;
104+
} else {
105+
emit("} or {");
106+
}
107+
increaseIndent();
108+
emit(out.label() + ";");
109+
emit(out.target());
110+
decreaseIndent();
111+
}
112+
if (state.isFinal()) {
113+
emit("} or { // final state");
114+
}
115+
emit("}");
116+
}
117+
}
118+
119+
/** The set of currently covered states. */
120+
private final Set<GraphState> covered = new HashSet<>();
121+
122+
/** Returns the singleton instance of this class. */
123+
static public LTS2ControlExporter instance() {
124+
return INSTANCE;
125+
}
126+
127+
/** The singleton instance of this class. */
128+
static private LTS2ControlExporter INSTANCE = new LTS2ControlExporter();
129+
}

src/main/java/nl/utwente/groove/lts/GTSFragment.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ public Set<GraphTransition> edgeSet() {
9292

9393
private final Set<GraphTransition> transitions = new LinkedHashSet<>();
9494

95+
/** Returns the start state of the GTS. */
96+
public GraphState startState() {
97+
return this.gts.startState();
98+
}
99+
95100
@Override
96101
public @NonNull GGraph<GraphState,GraphTransition> newGraph(String name) {
97102
return new GTSFragment(gts(), name);

0 commit comments

Comments
 (0)