1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.nuiton.eugene.plugin.writer;
24
25 import org.codehaus.plexus.component.annotations.Component;
26 import org.nuiton.eugene.models.object.ObjectModel;
27 import org.nuiton.eugene.models.state.StateModel;
28 import org.nuiton.eugene.writer.ChainedFileWriter;
29 import org.nuiton.eugene.writer.ChainedFileWriterConfiguration;
30 import org.nuiton.eugene.writer.WriterReport;
31 import org.nuiton.plugin.PluginHelper;
32 import org.nuiton.util.FasterCachedResourceResolver;
33 import org.nuiton.util.FileUtil;
34 import org.nuiton.util.Resource;
35 import org.nuiton.util.ResourceResolver;
36 import org.xml.sax.Attributes;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.helpers.DefaultHandler;
39
40 import javax.xml.parsers.ParserConfigurationException;
41 import javax.xml.parsers.SAXParser;
42 import javax.xml.parsers.SAXParserFactory;
43 import javax.xml.transform.Transformer;
44 import javax.xml.transform.TransformerFactory;
45 import javax.xml.transform.URIResolver;
46 import javax.xml.transform.stream.StreamResult;
47 import javax.xml.transform.stream.StreamSource;
48 import java.io.File;
49 import java.io.FileOutputStream;
50 import java.io.IOException;
51 import java.lang.reflect.Constructor;
52 import java.net.URL;
53 import java.util.List;
54 import java.util.Map;
55
56
57
58
59
60
61
62
63 @Component(role = ChainedFileWriter.class, hint = "xmi")
64 public class XmiChainedFileWriter extends BaseChainedFileWriter {
65
66 public static final String PROP_RESOLVER = "resolver";
67
68 public static final String PROP_FULL_PACKAGE_PATH = "fullPackagePath";
69
70 public static final String PROP_EXTRACTED_PACKAGES = "extraPackages";
71
72 public XmiChainedFileWriter() {
73 super(PROP_RESOLVER,
74 "resolver",
75 PROP_FULL_PACKAGE_PATH,
76 "fullPackagePath",
77 PROP_EXTRACTED_PACKAGES,
78 "extraPackages"
79 );
80 }
81
82 @Override
83 public boolean acceptModel(String modelType) {
84
85 return acceptObjectModelOrStateModel(modelType);
86 }
87
88 @Override
89 public String getInputProtocol() {
90 return "xmi";
91 }
92
93 @Override
94 public String getOutputProtocol(String modelType) {
95
96 return "model";
97 }
98
99 @Override
100 public boolean acceptInclude(String include) {
101 return include.startsWith("xmi:") || include.endsWith(".xmi") || include.endsWith(".uml");
102 }
103
104 @Override
105 public String getDefaultIncludes() {
106 return "**/*.xmi";
107 }
108
109 @Override
110 public String getDefaultInputDirectory() {
111 return "src/main/xmi";
112 }
113
114 @Override
115 public String getDefaultOutputDirectory() {
116 return "models";
117 }
118
119 @Override
120 public String getDefaultTestInputDirectory() {
121 return "src/test/xmi";
122 }
123
124 @Override
125 public String getDefaultTestOutputDirectory() {
126 return "test-models";
127 }
128
129 protected TransformerFactory transformerFactory;
130
131 protected TransformerFactory getTransformerFactory() {
132 if (transformerFactory == null) {
133 transformerFactory = TransformerFactory.newInstance();
134 }
135 return transformerFactory;
136 }
137
138 public String getFullPackagePath() {
139 return getProperty(PROP_FULL_PACKAGE_PATH, String.class);
140 }
141
142 public String getExtractedPackages() {
143 return getProperty(PROP_EXTRACTED_PACKAGES, String.class);
144 }
145
146 public String getResolver() {
147 return getProperty(PROP_RESOLVER, String.class);
148 }
149
150 @Override
151 public void generate(ChainedFileWriterConfiguration configuration,
152 File outputDirectory,
153 Map<File, List<File>> filesByRoot,
154 Map<File, List<File>> resourcesByFile) throws IOException {
155
156 if (configuration.isVerbose()) {
157 getLog().info(" with fullPackagePath : " + getFullPackagePath());
158 getLog().info(" with resolver : " + getResolver());
159 }
160
161 for (Map.Entry<File, List<File>> entry : filesByRoot.entrySet()) {
162 File inputDirectory = entry.getKey();
163 List<File> files = entry.getValue();
164
165 if (configuration.isVerbose()) {
166 getLog().info("Processing XSL tranformation on " +
167 inputDirectory + " for " + files.size() + " file(s).");
168 }
169
170 for (File file : files) {
171
172
173 actionXsl(configuration,
174 outputDirectory,
175 inputDirectory,
176 file
177 );
178
179
180
181
182
183
184
185
186 copyResources(configuration,
187 outputDirectory,
188 inputDirectory,
189 file,
190 resourcesByFile
191 );
192
193 }
194 }
195 }
196
197 protected boolean actionXsl(ChainedFileWriterConfiguration configuration,
198 File outputDirectory,
199 File inputDirectory,
200 File file) throws IOException {
201
202 try {
203 if (getLog().isDebugEnabled()) {
204 getLog().debug("treate file : " + file);
205 }
206
207 URIResolver fileResolver = getUriResolver(configuration, file);
208 String styleSheet =
209 getStyleSheet(configuration.getModelType(), file);
210 URL xsl = Resource.getURL(styleSheet);
211
212 String newExtension = configuration.getModelType();
213
214
215 File mirrorFile = FileUtil.getRelativeFile(inputDirectory,
216 outputDirectory,
217 file
218 );
219
220
221 File result = FileUtil.changeExtension(mirrorFile, newExtension);
222
223 if (!configuration.isOverwrite() &&
224 file.lastModified() < result.lastModified()) {
225
226 if (configuration.isVerbose()) {
227 getLog().info("Will not generate " + result +
228 " (up-to-date).");
229 }
230 return false;
231 }
232
233 PluginHelper.createDirectoryIfNecessary(result.getParentFile());
234 WriterReport writerReport = getWriterReport();
235 if (writerReport != null) {
236
237 writerReport.addFile(
238 getClass().getName(),
239 result,
240 false
241 );
242 }
243
244
245 Transformer transformer = getTransformerFactory().
246 newTransformer(new StreamSource(xsl.openStream()));
247
248 transformer.setParameter(PROP_FULL_PACKAGE_PATH,
249 getFullPackagePath()
250 );
251 transformer.setParameter(PROP_EXTRACTED_PACKAGES,
252 getExtractedPackages()
253 );
254
255 transformer.setURIResolver(fileResolver);
256 try (FileOutputStream output = new FileOutputStream(result)) {
257 transformer.transform(new StreamSource(file),
258 new StreamResult(output));
259 }
260 return true;
261 } catch (IOException e) {
262 throw e;
263 } catch (Exception e) {
264 throw new IOException(e.getMessage(), e);
265 }
266 }
267
268 protected URIResolver getUriResolver(
269 ChainedFileWriterConfiguration configuration, File model) {
270 URIResolver result = null;
271
272 try {
273 ClassLoader loader = configuration.getClassLoader();
274 Class<?> clazz = Class.forName(getResolver(), true, loader);
275
276
277 try {
278
279 Constructor<?> withBaseConstructor =
280 clazz.getConstructor(String.class);
281
282 String base = model.getParentFile().getAbsolutePath();
283
284 result = (URIResolver) withBaseConstructor.newInstance(base);
285 } catch (Exception eee) {
286 getLog().warn(
287 "Unable to instantiate resolver with String parameter",
288 eee);
289 }
290
291
292
293 if (result == null) {
294 result = (URIResolver) clazz.newInstance();
295 }
296
297 if (result instanceof ResourceResolver) {
298 ((ResourceResolver) result).setVerbose(
299 configuration.isVerbose());
300 ((ResourceResolver) result).setCl(loader);
301 if (result instanceof FasterCachedResourceResolver) {
302 boolean offline = configuration.isOffline();
303 if (getLog().isDebugEnabled()) {
304 getLog().debug("using offline mode ? : " + offline);
305 }
306 ((FasterCachedResourceResolver) result).setOffline(offline);
307 }
308 }
309
310 } catch (Exception eee) {
311 getLog().warn("Unable to instantiate resolver using " +
312 "the default constructor", eee);
313 }
314
315 return result;
316 }
317
318 protected String getStyleSheet(String modelType, File model) {
319 if (ObjectModel.NAME.equals(modelType)) {
320 String version = getXmiVersion(model);
321 String styleSheet = null;
322 if (version.startsWith("1.")) {
323 styleSheet = "xmi1.2ToObjectModel.xsl";
324 } else if (version.startsWith("2.")) {
325 styleSheet = "xmi2.1ToObjectModel.xsl";
326 } else {
327 getLog().error("Unsupported xmi version [" + version + "]");
328 }
329 return styleSheet;
330 }
331 if (StateModel.NAME.equals(modelType)) {
332
333 return "xmi1.2ToStateModel.xsl";
334 }
335
336 throw new IllegalStateException("unsupported modelType [" +
337 modelType + "]");
338 }
339
340
341
342
343
344
345
346 protected String getXmiVersion(File xmiFile) {
347 String version = null;
348
349 SAXParserFactory factory = SAXParserFactory.newInstance();
350
351 try {
352 SAXParser parser = factory.newSAXParser();
353
354 XmiVersionHandler handler = new XmiVersionHandler();
355 parser.parse(xmiFile, handler);
356
357 version = handler.getVersion();
358 } catch (ParserConfigurationException e) {
359 getLog().debug("Can't parse file as xmi", e);
360 } catch (SAXException e) {
361 getLog().debug("Can't parse file as xmi", e);
362 } catch (IOException e) {
363 getLog().debug("Can't parse file as xmi", e);
364 }
365
366 return version;
367 }
368
369
370 protected class XmiVersionHandler extends DefaultHandler {
371
372 protected String version;
373
374 public XmiVersionHandler() {
375 }
376
377 public String getVersion() {
378 return version;
379 }
380
381 @Override
382 public void startElement(String uri,
383 String localName,
384 String qName,
385 Attributes attributes) throws SAXException {
386
387 if (qName.equals("XMI")) {
388 version = attributes.getValue("xmi.version");
389 if (getLog().isDebugEnabled()) {
390 getLog().debug("XMI version found : " + version);
391 }
392 }
393
394 if (version == null) {
395 version = attributes.getValue("xmi:version");
396 if (getLog().isDebugEnabled()) {
397 getLog().debug("XMI version found : " + version);
398 }
399 }
400
401 }
402 }
403
404
405 }