Skip to content

Commit 137eb8a

Browse files
committed
Composite start graph refactored so as to enable better error reporting
1 parent f767522 commit 137eb8a

File tree

16 files changed

+319
-134
lines changed

16 files changed

+319
-134
lines changed

src/main/java/nl/utwente/groove/grammar/model/CompositeHostModel.java

Lines changed: 142 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,48 @@
22

33
import static nl.utwente.groove.grammar.model.ResourceKind.HOST;
44
import static nl.utwente.groove.grammar.model.ResourceKind.PROPERTIES;
5-
import static nl.utwente.groove.grammar.model.ResourceKind.TYPE;
65

7-
import java.util.HashMap;
8-
import java.util.Map;
6+
import java.util.ArrayList;
7+
import java.util.Collections;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Set;
911

1012
import org.eclipse.jdt.annotation.NonNull;
13+
import org.eclipse.jdt.annotation.NonNullByDefault;
14+
import org.eclipse.jdt.annotation.Nullable;
1115

12-
import nl.utwente.groove.grammar.QualName;
1316
import nl.utwente.groove.grammar.aspect.AspectGraph;
1417
import nl.utwente.groove.grammar.host.HostGraph;
15-
import nl.utwente.groove.util.parse.FormatError;
18+
import nl.utwente.groove.grammar.type.TypeLabel;
19+
import nl.utwente.groove.util.Factory;
1620
import nl.utwente.groove.util.parse.FormatErrorSet;
1721
import nl.utwente.groove.util.parse.FormatException;
1822

19-
/** Class to store the models that are used to compose the type graph. */
23+
/** Class compute the composed start graph. */
24+
@NonNullByDefault
2025
public class CompositeHostModel extends ResourceModel<HostGraph> {
2126
/**
2227
* Constructs a composite type model
2328
* @param grammar the underlying graph grammar
2429
*/
25-
CompositeHostModel(@NonNull GrammarModel grammar) {
26-
super(grammar, TYPE);
30+
CompositeHostModel(GrammarModel grammar) {
31+
this(grammar, null);
32+
}
33+
34+
/**
35+
* Constructs a composite type model from a single aspect graph.
36+
* @param grammar the underlying graph grammar
37+
* @param source the explicit source aspect graph; if {@code null}, the source
38+
* is implicit and derived from the active host graphs of the grammar.
39+
*/
40+
CompositeHostModel(GrammarModel grammar, @Nullable AspectGraph source) {
41+
super(grammar, HOST);
2742
setDependencies(PROPERTIES);
43+
this.implicit = source == null;
44+
this.hostModels = this.implicit
45+
? null // to be initialised later
46+
: Collections.<HostModel>singletonList(new HostModel(grammar, source));
2847
}
2948

3049
@Override
@@ -34,52 +53,144 @@ public class CompositeHostModel extends ResourceModel<HostGraph> {
3453
return result;
3554
}
3655

56+
/** Indicates if this composite model is implicit, i.e., derived from the active host graphs
57+
* in the grammar.
58+
*/
59+
public boolean isImplicit() {
60+
return this.implicit;
61+
}
62+
63+
private final boolean implicit;
64+
3765
@Override
38-
public Object getSource() {
39-
return null;
66+
public @Nullable AspectGraph getSource() {
67+
return this.source.get();
4068
}
4169

70+
/** The (lazily created) source aspect graph. */
71+
private final Factory<@Nullable AspectGraph> source = Factory.lazy(() -> {
72+
AspectGraph result = null;
73+
var hostModels = getHostModels();
74+
if (hostModels.size() == 1) {
75+
result = hostModels.getFirst().getSource();
76+
} else if (hostModels.size() > 1) {
77+
result = AspectGraph.mergeGraphs(getGrammar().getActiveGraphs(HOST));
78+
}
79+
return result;
80+
});
81+
82+
/** Returns the set of type labels in the combined source graphs. */
83+
public Set<TypeLabel> getTypeLabels() {
84+
return this.typeLabels.get();
85+
}
86+
87+
/**
88+
* Returns a mapping from the nodes in the model source to the corresponding
89+
* nodes in the resource that is constructed from it.
90+
* This method should only be called if the model contains no errors.
91+
* @return the mapping from source graph to resource elements
92+
*/
93+
public HostModelMap getMap() {
94+
var model = getCombinedModel();
95+
assert model != null;
96+
return model.getMap();
97+
}
98+
99+
private final Factory<Set<TypeLabel>> typeLabels = Factory.lazy(() -> {
100+
var result = new HashSet<TypeLabel>();
101+
for (var model : getHostModels()) {
102+
result.addAll(model.getTypeLabels());
103+
}
104+
return result;
105+
});
106+
42107
@Override
43108
public String getName() {
44-
return "Composite host graph";
109+
var source = getSource();
110+
return source == null
111+
? ""
112+
: source.getName();
45113
}
46114

47-
/** Indicates if this model is composed of more than one underlying host model. */
48-
public boolean isMultiple() {
49-
return getGrammar().getActiveNames(HOST).size() > 1;
115+
@Override
116+
void notifyWillRebuild() {
117+
super.notifyWillRebuild();
118+
if (isImplicit()) {
119+
this.hostModels = null;
120+
this.combinedModel.reset();
121+
this.source.reset();
122+
this.typeLabels.reset();
123+
}
50124
}
51125

52126
@Override
53127
HostGraph compute() throws FormatException {
54-
var hostModelMap = computeHostModelMap();
55-
if (hostModelMap.isEmpty()) {
128+
HostGraph result = null;
129+
var hostModels = getHostModels();
130+
FormatErrorSet errors = new FormatErrorSet();
131+
for (var model : hostModels) {
132+
for (var error : model.getErrors()) {
133+
errors.add("Error in graph '%s': %s", model.getName(), error, model.getSource());
134+
}
135+
}
136+
errors.throwException();
137+
var combinedModel = getCombinedModel();
138+
if (combinedModel == null) {
56139
throw new FormatException("No active start graph");
57140
} else {
58-
AspectGraph startGraph = AspectGraph.mergeGraphs(getGrammar().getActiveGraphs(HOST));
59-
return new HostModel(getGrammar(), startGraph).compute();
141+
try {
142+
result = combinedModel.toHost();
143+
} catch (FormatException exc) {
144+
for (var error : exc.getErrors()) {
145+
errors.add("Error in composite host graph: %s", error);
146+
}
147+
errors.throwException();
148+
}
60149
}
150+
assert result != null;
151+
return result;
61152
}
62153

154+
/** Returns the combined host model.
155+
* This is either {@code null} of there is no active host model, or the singular host model, or a new
156+
* host model obtained from the merged sources of the active host models, if there are multiple.
157+
*/
158+
private @Nullable HostModel getCombinedModel() {
159+
return this.combinedModel.get();
160+
}
161+
162+
/** The combined host model.
163+
* @see #getCombinedModel()
164+
*/
165+
private final Factory<@Nullable HostModel> combinedModel = Factory.lazy(() -> {
166+
HostModel result = null;
167+
var hostModels = getHostModels();
168+
if (hostModels.size() == 1) {
169+
result = hostModels.getFirst();
170+
} else {
171+
result = new HostModel(getGrammar(), getSource());
172+
173+
}
174+
return result;
175+
});
176+
63177
/**
64-
* Computes the mapping from names to active host models.
65-
* @throws FormatException if there are format errors in the active type models
178+
* Returns the list of active host models.
66179
*/
67-
private Map<QualName,HostModel> computeHostModelMap() throws FormatException {
68-
FormatErrorSet errors = new FormatErrorSet();
69-
Map<QualName,HostModel> result = new HashMap<>();
70-
for (var activeHostName : getGrammar().getActiveNames(HOST)) {
71-
ResourceModel<?> hostModel = getGrammar().getResource(HOST, activeHostName);
72-
result.put(activeHostName, (HostModel) hostModel);
73-
for (FormatError error : hostModel.getErrors()) {
74-
errors
75-
.add("Error in host graph '%s': %s", activeHostName, error,
76-
hostModel.getSource());
180+
private List<HostModel> getHostModels() {
181+
var result = this.hostModels;
182+
if (result == null) {
183+
result = new ArrayList<>();
184+
for (var name : getGrammar().getActiveNames(HOST)) {
185+
var model = getGrammar().getResource(HOST, name);
186+
result.add((HostModel) model);
77187
}
78188
}
79-
errors.throwException();
80189
return result;
81190
}
82191

192+
private @Nullable List<HostModel> hostModels;
193+
83194
/** Fixed name for the composite host model. */
84195
static public final String NAME = "composite-host";
85196
}

src/main/java/nl/utwente/groove/grammar/model/CompositeTypeModel.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import static nl.utwente.groove.grammar.model.ResourceKind.RULE;
66
import static nl.utwente.groove.grammar.model.ResourceKind.TYPE;
77

8+
import java.util.ArrayList;
89
import java.util.EnumSet;
910
import java.util.HashMap;
1011
import java.util.HashSet;
12+
import java.util.List;
1113
import java.util.Map;
1214
import java.util.Set;
1315

@@ -118,20 +120,20 @@ boolean isShouldRebuild() {
118120
@Override
119121
TypeGraph compute() throws FormatException {
120122
TypeGraph result = null;
121-
Map<QualName,TypeModel> typeModelMap = computeTypeModelMap();
123+
var typeModels = computeTypeModels();
122124
// first test if there is something to be done
123-
if (typeModelMap.isEmpty()) {
125+
if (typeModels.isEmpty()) {
124126
result = getImplicitTypeGraph();
125-
} else if (typeModelMap.size() == 1) {
126-
result = typeModelMap.values().iterator().next().toResource();
127+
} else if (typeModels.size() == 1) {
128+
result = typeModels.iterator().next().toResource();
127129
} else {
128130
result = new TypeGraph(QualName.name(NAME));
129131
FormatErrorSet errors = createErrors();
130132
// There are no errors in each of the models, try to compose the
131133
// type graph.
132134
Map<TypeNode,TypeNode> nodeMergeMap = new HashMap<>();
133135
Map<TypeNode,TypeModel> importModels = new HashMap<>();
134-
for (TypeModel model : typeModelMap.values()) {
136+
for (TypeModel model : typeModels) {
135137
try {
136138
TypeGraph graph = model.toResource();
137139
nodeMergeMap.putAll(result.add(graph));
@@ -176,18 +178,18 @@ TypeGraph compute() throws FormatException {
176178
* Computes the mapping from names to active type models.
177179
* @throws FormatException if there are format errors in the active type models
178180
*/
179-
private Map<QualName,TypeModel> computeTypeModelMap() throws FormatException {
181+
private List<TypeModel> computeTypeModels() throws FormatException {
180182
FormatErrorSet errors = new FormatErrorSet();
181-
Map<QualName,TypeModel> typeModelMap = new HashMap<>();
183+
var result = new ArrayList<TypeModel>();
182184
for (QualName activeTypeName : getGrammar().getActiveNames(TYPE)) {
183185
ResourceModel<?> typeModel = getGrammar().getResource(TYPE, activeTypeName);
184-
typeModelMap.put(activeTypeName, (TypeModel) typeModel);
186+
result.add((TypeModel) typeModel);
185187
for (FormatError error : typeModel.getErrors()) {
186188
errors.add("Error in type '%s': %s", activeTypeName, error, typeModel.getSource());
187189
}
188190
}
189191
errors.throwException();
190-
return typeModelMap;
192+
return result;
191193
}
192194

193195
/**
@@ -199,30 +201,30 @@ private TypeGraph getImplicitTypeGraph() {
199201

200202
/** The implicit type graph. */
201203
private final Factory<TypeGraph> implicitTypeGraph
202-
= Factory.lazy(() -> ImplicitTypeGraph.newInstance(getLabels()));
204+
= Factory.lazy(() -> ImplicitTypeGraph.newInstance(getTypeLabels()));
203205

204206
@Override
205207
void notifyWillRebuild() {
206208
this.implicitTypeGraph.reset();
207209
}
208210

209211
/**
210-
* Computes the set of all labels occurring in the rules and host graph.
212+
* Computes the set of all type labels occurring in the rules and host graph.
211213
* This is used to construct the implicit type graph,
212214
* if no type graphs are enabled.
213215
*/
214-
private Set<TypeLabel> getLabels() {
216+
private Set<TypeLabel> getTypeLabels() {
215217
Set<TypeLabel> result = new HashSet<>();
216218
// get the labels from the rules and host graphs
217219
for (ResourceKind kind : EnumSet.of(RULE, HOST)) {
218220
for (ResourceModel<?> model : getGrammar().getResourceSet(kind)) {
219-
result.addAll(((GraphBasedModel<?>) model).getLabels());
221+
result.addAll(((GraphBasedModel<?>) model).getTypeLabels());
220222
}
221223
}
222224
// get the labels from the external start graph
223-
HostModel host = getGrammar().getStartGraphModel();
225+
var host = getGrammar().getStartGraphModel();
224226
if (host != null) {
225-
result.addAll(host.getLabels());
227+
result.addAll(host.getTypeLabels());
226228
}
227229
return result;
228230
}

0 commit comments

Comments
 (0)