20
20
import java .text .MessageFormat ;
21
21
import java .util .ArrayList ;
22
22
import java .util .HashSet ;
23
- import java .util .LinkedList ;
24
23
import java .util .List ;
25
24
import java .util .Map ;
26
25
import java .util .Set ;
52
51
* @author Oliver Libutzki - Added reloadAllModelsOfType method
53
52
* @author Simon Kaufmann - added validation of models before loading them
54
53
* @author Laurent Garnier - Added method generateSyntaxFromModel
54
+ * @author Laurent Garnier - Added method createTemporaryModel
55
55
*/
56
56
@ Component (immediate = true )
57
57
@ NonNullByDefault
58
58
public class ModelRepositoryImpl implements ModelRepository {
59
59
60
+ private static final String PREFIX_TMP_MODEL = "tmp_" ;
61
+
60
62
private final Logger logger = LoggerFactory .getLogger (ModelRepositoryImpl .class );
61
63
private final ResourceSet resourceSet ;
62
64
private final Map <String , String > resourceOptions = Map .of (XtextResource .OPTION_ENCODING ,
@@ -87,32 +89,52 @@ public ModelRepositoryImpl(final @Reference SafeEMF safeEmf) {
87
89
if (!resource .getContents ().isEmpty ()) {
88
90
return resource .getContents ().getFirst ();
89
91
} else {
90
- logger .warn ("Configuration model '{}' is either empty or cannot be parsed correctly!" , name );
92
+ logger .warn ("DSL model '{}' is either empty or cannot be parsed correctly!" , name );
91
93
resourceSet .getResources ().remove (resource );
92
94
return null ;
93
95
}
94
96
} else {
95
- logger .trace ("Configuration model '{}' can not be found" , name );
97
+ logger .trace ("DSL model '{}' can not be found" , name );
96
98
return null ;
97
99
}
98
100
}
99
101
}
100
102
101
103
@ Override
102
104
public boolean addOrRefreshModel (String name , final InputStream originalInputStream ) {
103
- logger .info ("Loading model '{}'" , name );
105
+ return addOrRefreshModel (name , originalInputStream , null , null );
106
+ }
107
+
108
+ public boolean addOrRefreshModel (String name , final InputStream originalInputStream , @ Nullable List <String > errors ,
109
+ @ Nullable List <String > warnings ) {
110
+ logger .info ("Loading DSL model '{}'" , name );
104
111
Resource resource = null ;
105
112
byte [] bytes ;
106
113
try (InputStream inputStream = originalInputStream ) {
107
114
bytes = inputStream .readAllBytes ();
108
- String validationResult = validateModel (name , new ByteArrayInputStream (bytes ));
109
- if (validationResult != null ) {
110
- logger .warn ("Configuration model '{}' has errors, therefore ignoring it: {}" , name , validationResult );
115
+ List <String > newErrors = new ArrayList <>();
116
+ List <String > newWarnings = new ArrayList <>();
117
+ boolean valid = validateModel (name , new ByteArrayInputStream (bytes ), newErrors , newWarnings );
118
+ if (errors != null ) {
119
+ errors .addAll (newErrors );
120
+ }
121
+ if (warnings != null ) {
122
+ warnings .addAll (newWarnings );
123
+ }
124
+ if (!valid ) {
125
+ logger .warn ("DSL model '{}' has errors, therefore ignoring it: {}" , name , String .join ("\n " , newErrors ));
111
126
removeModel (name );
112
127
return false ;
113
128
}
129
+ if (!newWarnings .isEmpty ()) {
130
+ logger .info ("Validation issues found in DSL model '{}', using it anyway:\n {}" , name ,
131
+ String .join ("\n " , newWarnings ));
132
+ }
114
133
} catch (IOException e ) {
115
- logger .warn ("Configuration model '{}' cannot be parsed correctly!" , name , e );
134
+ if (errors != null ) {
135
+ errors .add ("Model cannot be parsed correctly: %s" .formatted (e .getMessage ()));
136
+ }
137
+ logger .warn ("DSL model '{}' cannot be parsed correctly!" , name , e );
116
138
return false ;
117
139
}
118
140
try (InputStream inputStream = new ByteArrayInputStream (bytes )) {
@@ -144,7 +166,10 @@ public boolean addOrRefreshModel(String name, final InputStream originalInputStr
144
166
}
145
167
}
146
168
} catch (IOException e ) {
147
- logger .warn ("Configuration model '{}' cannot be parsed correctly!" , name , e );
169
+ if (errors != null ) {
170
+ errors .add ("Model cannot be parsed correctly: %s" .formatted (e .getMessage ()));
171
+ }
172
+ logger .warn ("DSL model '{}' cannot be parsed correctly!" , name , e );
148
173
if (resource != null ) {
149
174
resourceSet .getResources ().remove (resource );
150
175
}
@@ -176,7 +201,7 @@ public Iterable<String> getAllModelNamesOfType(final String modelType) {
176
201
return resourceListCopy .stream ()
177
202
.filter (input -> input .getURI ().lastSegment ().contains ("." ) && input .isLoaded ()
178
203
&& modelType .equalsIgnoreCase (input .getURI ().fileExtension ())
179
- && !input .getURI ().lastSegment (). startsWith ( "tmp_" ))
204
+ && !isTemporaryModel ( input .getURI ().lastSegment ()))
180
205
.map (from -> from .getURI ().path ()).toList ();
181
206
}
182
207
}
@@ -189,7 +214,7 @@ public void reloadAllModelsOfType(final String modelType) {
189
214
for (Resource resource : resourceListCopy ) {
190
215
if (resource .getURI ().lastSegment ().contains ("." ) && resource .isLoaded ()
191
216
&& modelType .equalsIgnoreCase (resource .getURI ().fileExtension ())
192
- && !resource .getURI ().lastSegment (). startsWith ( "tmp_" )) {
217
+ && !isTemporaryModel ( resource .getURI ().lastSegment ())) {
193
218
XtextResource xtextResource = (XtextResource ) resource ;
194
219
// It's not sufficient to discard the derived state.
195
220
// The quick & dirts solution is to reparse the whole resource.
@@ -211,7 +236,7 @@ public Set<String> removeAllModelsOfType(final String modelType) {
211
236
for (Resource resource : resourceListCopy ) {
212
237
if (resource .getURI ().lastSegment ().contains ("." ) && resource .isLoaded ()
213
238
&& modelType .equalsIgnoreCase (resource .getURI ().fileExtension ())
214
- && !resource .getURI ().lastSegment (). startsWith ( "tmp_" )) {
239
+ && !isTemporaryModel ( resource .getURI ().lastSegment ())) {
215
240
logger .debug ("Removing resource '{}'" , resource .getURI ().lastSegment ());
216
241
ret .add (resource .getURI ().lastSegment ());
217
242
resourceSet .getResources ().remove (resource );
@@ -232,16 +257,27 @@ public void removeModelRepositoryChangeListener(ModelRepositoryChangeListener li
232
257
listeners .remove (listener );
233
258
}
234
259
260
+ @ Override
261
+ public @ Nullable String createTemporaryModel (String modelType , InputStream inputStream , List <String > errors ,
262
+ List <String > warnings ) {
263
+ String name = "%smodel_%d.%s" .formatted (PREFIX_TMP_MODEL , ++counter , modelType );
264
+ return addOrRefreshModel (name , inputStream , errors , warnings ) ? name : null ;
265
+ }
266
+
267
+ private boolean isTemporaryModel (String modelName ) {
268
+ return modelName .startsWith (PREFIX_TMP_MODEL );
269
+ }
270
+
235
271
@ Override
236
272
public void generateSyntaxFromModel (OutputStream out , String modelType , EObject modelContent ) {
237
273
synchronized (resourceSet ) {
238
- String name = "tmp_generated_syntax_% d.%s" .formatted (++counter , modelType );
274
+ String name = "%sgenerated_syntax_% d.%s" .formatted (PREFIX_TMP_MODEL , ++counter , modelType );
239
275
Resource resource = resourceSet .createResource (URI .createURI (name ));
240
276
try {
241
277
resource .getContents ().add (modelContent );
242
278
resource .save (out , Map .of (XtextResource .OPTION_ENCODING , StandardCharsets .UTF_8 .name ()));
243
279
} catch (IOException e ) {
244
- logger .warn ("Exception when saving the model {}" , resource .getURI ().lastSegment ());
280
+ logger .warn ("Exception when saving DSL model {}" , resource .getURI ().lastSegment ());
245
281
} finally {
246
282
resourceSet .getResources ().remove (resource );
247
283
}
@@ -268,28 +304,28 @@ public void generateSyntaxFromModel(OutputStream out, String modelType, EObject
268
304
* Validation will be done on a separate resource, in order to keep the original one intact in case its content
269
305
* needs to be removed because of syntactical errors.
270
306
*
271
- * @param name
272
- * @param inputStream
273
- * @return error messages as a String if any syntactical error were found, <code>null</code> otherwise
307
+ * @param name the model name
308
+ * @param inputStream an input stream with the model's content
309
+ * @param errors the list to be used to fill the errors
310
+ * @param warnings the list to be used to fill the warnings
311
+ * @return false if any syntactical error were found, false otherwise
274
312
* @throws IOException if there was an error with the given {@link InputStream}, loading the resource from there
275
313
*/
276
- private @ Nullable String validateModel (String name , InputStream inputStream ) throws IOException {
314
+ private boolean validateModel (String name , InputStream inputStream , List <String > errors , List <String > warnings )
315
+ throws IOException {
277
316
// use another resource for validation in order to keep the original one for emergency-removal in case of errors
278
- Resource resource = resourceSet .createResource (URI .createURI ("tmp_" + name ));
317
+ Resource resource = resourceSet .createResource (URI .createURI (PREFIX_TMP_MODEL + name ));
279
318
try {
280
319
resource .load (inputStream , resourceOptions );
281
- StringBuilder criticalErrors = new StringBuilder ();
282
- List <String > warnings = new LinkedList <>();
283
320
284
321
if (!resource .getContents ().isEmpty ()) {
285
322
// Check for syntactical errors
286
323
for (Diagnostic diagnostic : resource .getErrors ()) {
287
- criticalErrors
288
- .append (MessageFormat .format ("[{0},{1}]: {2}\n " , Integer .toString (diagnostic .getLine ()),
289
- Integer .toString (diagnostic .getColumn ()), diagnostic .getMessage ()));
324
+ errors .add (MessageFormat .format ("[{0},{1}]: {2}" , Integer .toString (diagnostic .getLine ()),
325
+ Integer .toString (diagnostic .getColumn ()), diagnostic .getMessage ()));
290
326
}
291
- if (!criticalErrors .isEmpty ()) {
292
- return criticalErrors . toString () ;
327
+ if (!resource . getErrors () .isEmpty ()) {
328
+ return false ;
293
329
}
294
330
295
331
// Check for validation errors, but log them only
@@ -299,10 +335,6 @@ public void generateSyntaxFromModel(OutputStream out, String modelType, EObject
299
335
for (org .eclipse .emf .common .util .Diagnostic d : diagnostic .getChildren ()) {
300
336
warnings .add (d .getMessage ());
301
337
}
302
- if (!warnings .isEmpty ()) {
303
- logger .info ("Validation issues found in configuration model '{}', using it anyway:\n {}" , name ,
304
- String .join ("\n " , warnings ));
305
- }
306
338
} catch (NullPointerException e ) {
307
339
// see https://github.com/eclipse/smarthome/issues/3335
308
340
logger .debug ("Validation of '{}' skipped due to internal errors." , name );
@@ -311,12 +343,14 @@ public void generateSyntaxFromModel(OutputStream out, String modelType, EObject
311
343
} finally {
312
344
resourceSet .getResources ().remove (resource );
313
345
}
314
- return null ;
346
+ return true ;
315
347
}
316
348
317
349
private void notifyListeners (String name , EventType type ) {
318
- for (ModelRepositoryChangeListener listener : listeners ) {
319
- listener .modelChanged (name , type );
350
+ if (!isTemporaryModel (name )) {
351
+ for (ModelRepositoryChangeListener listener : listeners ) {
352
+ listener .modelChanged (name , type );
353
+ }
320
354
}
321
355
}
322
356
}
0 commit comments