Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 78e752205e24da8d322525bdaafa4214992f0e70 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*******************************************************************************
 * Copyright (c) 2011 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 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * CONTRIBUTORS:
 * 		Thomas Schuetz and Henrik Rentz-Reichert (initial contribution)
 * 
 *******************************************************************************/

package org.eclipse.etrice.generator.base;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.etrice.core.fsm.fSM.DetailCode;
import org.eclipse.etrice.core.genmodel.builder.GeneratorModelBuilder;
import org.eclipse.etrice.core.genmodel.etricegen.ExpandedActorClass;
import org.eclipse.etrice.core.genmodel.etricegen.Root;
import org.eclipse.etrice.core.genmodel.fsm.FsmGenExtensions;
import org.eclipse.etrice.core.genmodel.fsm.IDiagnostician;
import org.eclipse.etrice.core.room.DataClass;
import org.eclipse.etrice.core.room.ProtocolClass;
import org.eclipse.etrice.core.room.RoomModel;
import org.eclipse.etrice.generator.base.args.Arguments;
import org.eclipse.etrice.generator.base.io.IGeneratorFileIO;
import org.eclipse.etrice.generator.base.io.ILineOutput;
import org.eclipse.etrice.generator.base.io.LineOutput;
import org.eclipse.etrice.generator.base.logging.ILogger;
import org.eclipse.etrice.generator.fsm.generic.IDetailCodeTranslator;
import org.eclipse.etrice.generator.generic.RoomExtensions;
import org.eclipse.etrice.generator.generic.TestInstanceCreator;

import com.google.inject.Inject;
import com.google.inject.Module;

/**
 * A base class for generators of ROOM models.
 * 
 * <p>
 * As examples see
 * <ul>
 *   <li>the {@link org.eclipse.etrice.generator.c.Main C generator}</li>
 *   <li>the {@link org.eclipse.etrice.generator.cpp.Main C++ generator}</li>
 *   <li>the {@link org.eclipse.etrice.generator.java.Main Java generator}</li>
 * </ul>
 * </p>
 * 
 * @author Henrik Rentz-Reichert
 *
 */
public abstract class AbstractGenerator implements IGenerator, IDetailCodeTranslator {

	/**
	 * constant used as return value of {@link #runGenerator())}
	 * @see #GENERATOR_ERROR
	 */
	public static final int GENERATOR_OK = 0;
	
	/**
	 * constant used as return value of {@link #runGenerator()}
	 * @see #GENERATOR_OK
	 */
	public static final int GENERATOR_ERROR = 1;
	
	protected static ILineOutput output = new LineOutput();
	private static AbstractGenerator instance = null;
	private static Arguments settings = null;
	
	/**
	 * It is assumed (though not enforced) that the generator is a singleton.
	 * 
	 * @return the singleton {@link AbstractGenerator}
	 */
	public static AbstractGenerator getInstance() {
		return instance;
	}
	
	/**
	 * The generator settings can also be statically accessed using {@link #getInstance()} followed
	 * by a call to this method.
	 * 
	 * @return the {@link #generatorSettings}
	 */
	public static Arguments getSettings() {
		return settings;
	}
	
	/**
	 * This method can be used to achieve different output behavior: in stand alone mode this might
	 * be just the console, inside Eclipse this rather would be the console view
	 * 
	 * @param out an {@link ILineOutput}
	 */
	public static void setOutput(ILineOutput out) {
		if (out != null)
			output = out;
	}
	
	/**
	 * creates an instance of the generator and invokes the {@link #runGenerator(String[])} method
	 * @param generatorModule a Guice module from which the {@link com.google.inject.Injector Injector} is created
	 * @param args the command line arguments
	 * @return GENERATOR_OK or GENERATOR_ERROR
	 */
	public static int createAndRunGenerator(Module generatorModule, String[] args) {
		int ret = GENERATOR_OK;
		GeneratorApplication genAppl = GeneratorApplication.create(generatorModule);
		try {
			genAppl.run(args, output);
		}
		catch(GeneratorException e) {
			ret = GENERATOR_ERROR;
		}
		return ret;
	}
	
	/**
	 * The injected diagnostician
	 */
	@Inject
	protected IDiagnostician diagnostician;
	
	/**
	 * The injected translation provider
	 */
	@Inject
	protected ITranslationProvider translationProvider;
	
	private HashMap<DetailCode, String> detailcode2string = new HashMap<DetailCode, String>();
	private ResourceSet resourceSet = null;
	
	/**
	 * The protected constructor is setting the {@link #instance} static member
	 */
	protected AbstractGenerator() {
		instance = this;
	}
	
	@Override
	public void generate(List<Resource> resources, Arguments arguments, IGeneratorFileIO fileIO, ILogger logger) {
		AbstractGenerator.settings = arguments;
		RoomExtensions.setGenDir(arguments.get(AbstractGeneratorOptions.GEN_DIR));
		RoomExtensions.setGenInfoDir(arguments.get(AbstractGeneratorOptions.GEN_INFO_DIR));
		RoomExtensions.setGenDocDir(arguments.get(AbstractGeneratorOptions.GEN_DOC_DIR));
		
		if(resources.isEmpty()) {
			logger.logError("no input files");
			logger.logInfo("-- terminating");
			throw new GeneratorException("can't determine resource set without input files");
		}
		resourceSet = resources.get(0).getResourceSet();
		
		int ret = runGenerator(resources, arguments, fileIO, logger);
		if(ret == GENERATOR_OK) {
			logger.logInfo("-- finished");
		}
		else {
			logger.logInfo("-- terminating");
			throw new GeneratorException("generator error");
		}
	}
	
	/**
	 * abstract method which is finally called by {@link #createAndRunGenerator(Module, String[])}
	 * @param resources a list of the main models
	 * @param arguments the generator arguments
	 * @return GENERATOR_OK or GENERATOR_ERROR
	 */
	protected abstract int runGenerator(List<Resource> resources, Arguments arguments, IGeneratorFileIO fileIO, ILogger logger);

	/**
	 * This resource set combines all resources processed by the generator
	 * @return the resource set for the input models
	 */
	protected ResourceSet getResourceSet() {
		return resourceSet;
	}

	/**
	 * @param resources the list of models
	 * @param arguments the generator arguments
	 * @return the {@link Root} object of the generator model (is added to a new Resource also)
	 */
	protected Root createGeneratorModel(List<Resource> resources, Arguments arguments, ILogger logger) {
		boolean doTranslate = !arguments.get(AbstractGeneratorOptions.NOTRANSLATE);
		boolean asLibrary = arguments.get(AbstractGeneratorOptions.LIB);
		String genModelPath = arguments.get(AbstractGeneratorOptions.SAVE_GEN_MODEL);
		Set<URI> mainModelURIs = resources.stream().map(m -> m.getURI()).collect(Collectors.toSet());
		
		// create instance and mapping for test instances
		List<Resource> testInstanceResources = new TestInstanceCreator(logger, diagnostician).createInstancesAndMapping(
				mainModelURIs, getResourceSet());
		if (testInstanceResources==null) {
			return null;
		}
		
		// create a list of ROOM models
		List<RoomModel> mainModels = new ArrayList<RoomModel>();
		List<RoomModel> importedModels = new ArrayList<RoomModel>();
		for (Resource resource : getResourceSet().getResources()) {
			boolean isMainModel = mainModelURIs.contains(resource.getURI());
			for (EObject content : resource.getContents()){
				if (content instanceof RoomModel) {
					if (isMainModel) {
						mainModels.add((RoomModel) content);
					}
					else {
						importedModels.add((RoomModel) content);
					}
				}
			}
		}
		if (!testInstanceResources.isEmpty()) {
			for (Resource resource : testInstanceResources) {
				for (EObject content : resource.getContents()){
					if (content instanceof RoomModel) {
						mainModels.add((RoomModel) content);
					}
				}
			}
		}
		if (importedModels.isEmpty() && mainModels.isEmpty()) {
			logger.logError("no ROOM models found");
			return null;
		}
		else {			
			logger.logInfo("-- creating generator model");
			GeneratorModelBuilder gmb = new GeneratorModelBuilder(logger, diagnostician);
			Root gmRoot = gmb.createGeneratorModel(mainModels, importedModels, asLibrary);
			if (diagnostician.isFailed()) {
				logger.logError("validation failed during build of generator model");
				return null;
			}
			
			translateDetailCodes(gmRoot, doTranslate);
			
			URI genModelURI = !genModelPath.isEmpty() ? URI.createFileURI(genModelPath) : URI.createFileURI("tmp.rim");
			Resource genResource = getResourceSet().createResource(genModelURI);
			genResource.getContents().add(gmRoot);
			if (!genModelPath.isEmpty()) {
				try {
					logger.logInfo("saving genmodel to "+genModelPath);
					genResource.save(Collections.EMPTY_MAP);
				}
				catch (IOException e) {
					logger.logError(e.getMessage());
					return null;
				}
			}
			return gmRoot;
		}
	}

	/**
	 * Create detail code translations once and for all.
	 * This method is called by {@link #createGeneratorModel(boolean, String)} after all models
	 * have been read and validated and the transformation into a generator model succeeded.
	 * 
	 * <p>
	 * The translation of a detail code can be looked up from a map by a call to
	 * {@link #getTranslatedCode(DetailCode)}.
	 * </p>
	 * 
	 * @param gmRoot
	 */
	protected void translateDetailCodes(Root gmRoot, boolean doTranslate) {
		
		for (ExpandedActorClass xpac : gmRoot.getXpActorClasses()) {
			DetailCodeTranslator dct = new DetailCodeTranslator(xpac.getActorClass(), translationProvider, doTranslate);
			translateDetailCodesOfTree(xpac.getActorClass(), dct);
			List<DetailCode> allDetailCodes = FsmGenExtensions.getAllDetailCodes(xpac.getGraphContainer().getGraph());
			for (DetailCode dc : allDetailCodes) {
				detailcode2string.put(dc, dct.translateDetailCode(dc));
			}
		}
		
		for (DataClass dc : gmRoot.getDataClasses()) {
			DetailCodeTranslator dct = new DetailCodeTranslator(dc, translationProvider, doTranslate);
			translateDetailCodesOfTree(dc, dct);
		}
		
		for (ProtocolClass pc : gmRoot.getProtocolClasses()) {
			if (pc.getConjugated()!=null) {
				DetailCodeTranslator dct = new DetailCodeTranslator(pc.getConjugated(), translationProvider, doTranslate);
				translateDetailCodesOfTree(pc.getConjugated(), dct);
			}
			if (pc.getRegular()!=null) {
				DetailCodeTranslator dct = new DetailCodeTranslator(pc.getRegular(), translationProvider, doTranslate);
				translateDetailCodesOfTree(pc.getRegular(), dct);
			}
			
			DetailCodeTranslator dct = new DetailCodeTranslator(pc, translationProvider, doTranslate);
			translateDetailCodesOfTree(pc.getUserCode1(), dct);
			translateDetailCodesOfTree(pc.getUserCode2(), dct);
			translateDetailCodesOfTree(pc.getUserCode3(), dct);
		}
	}
	
	/**
	 * Creates translations for all {@link DetailCode}s found in the {@link EObject#eAllContents()}.
	 * 
	 * @param container the root of an {@link EObject} containment tree that should be translated
	 * @param dct the {@link DetailCodeTranslator} to apply
	 */
	protected void translateDetailCodesOfTree(EObject container, DetailCodeTranslator dct) {
		if (container==null)
			return;
		if (container instanceof DetailCode) {
			DetailCode dc = (DetailCode) container;
			detailcode2string.put(dc, dct.translateDetailCode(dc));
			return;
		}
		
		TreeIterator<EObject> it = container.eAllContents();
		while (it.hasNext()) {
			EObject obj = it.next();
			if (obj instanceof DetailCode) {
				DetailCode dc = (DetailCode) obj;
				detailcode2string.put(dc, dct.translateDetailCode(dc));
			}
		}
	}

	/**
	 * @param dc a {@link DetailCode}
	 * @return the mapped result of the translation or an empty string
	 */
	public String getTranslatedCode(DetailCode dc) {
		String code = detailcode2string.get(dc);
		if (code==null)
			return "";
		return code;
	}

}

Back to the top