View Javadoc
1   /*
2    * #%L
3    * EUGene :: Maven plugin
4    * %%
5    * Copyright (C) 2006 - 2010 CodeLutin
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
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   * Implentation pour les writer to type xmi (qui transforme du xmi via xsl
58   * vers du model).
59   *
60   * @author tchemit
61   * @since 2.0.0
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          // supported by objectModel and stateModel
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          // next writer : write from model files
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                 // lancement des traitements xsl sur les fichiers trouvés dans le repertoire
173                 actionXsl(configuration,
174                           outputDirectory,
175                           inputDirectory,
176                           file
177                 );
178 
179 //                if (!reacted) {
180 //
181 //                    // file was not treated, nothing else to do
182 //                    continue;
183 //                }
184 
185                 // copy resources associated with the file
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             // Prepare resolver, stylesheet
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             // get the mirror file in the ouput root directory
215             File mirrorFile = FileUtil.getRelativeFile(inputDirectory,
216                                                        outputDirectory,
217                                                        file
218             );
219 
220             // change the extension name to the modeltype
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             // Create the xsl transformer and set parameters
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             // Try to set the base using the constructor
277             try {
278                 // Look for a constructor with a String parameter (base)
279                 Constructor<?> withBaseConstructor =
280                         clazz.getConstructor(String.class);
281                 // Set the xmi folder as the base
282                 String base = model.getParentFile().getAbsolutePath();
283                 // Instantiate
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             // If resolver is still not created, create it using the default
292             // constructor
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             //TODO when StateModel will be supported in 2.1, compute the version to resolve the correct stylesheet
333             return "xmi1.2ToStateModel.xsl";
334         }
335 
336         throw new IllegalStateException("unsupported modelType [" +
337                                         modelType + "]");
338     }
339 
340     /**
341      * Try to find xmi version on a file.
342      *
343      * @param xmiFile file to inspect
344      * @return version or null if version can't have been found
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     /** Sax handler to find xmi version into xmi document. */
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 }