View Javadoc
1   /*
2    * #%L
3    * EUGene :: EUGene
4    * %%
5    * Copyright (C) 2004 - 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.writer;
24  
25  import com.google.common.base.Preconditions;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.codehaus.plexus.component.annotations.Component;
29  import org.nuiton.eugene.models.extension.tagvalue.provider.TagValueMetadatasProvider;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.LinkedHashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.regex.Matcher;
41  import java.util.regex.Pattern;
42  
43  /**
44   * Default implementation of the {@link ChainedWriterEngine}.
45   *
46   * Created: 17 déc. 2009
47   *
48   * @author Tony Chemit - chemit@codelutin.com
49   * @since 2.0.0
50   */
51  @Component(role = ChainedWriterEngine.class, hint = "default")
52  public class DefaultChainedWriterEngine implements ChainedWriterEngine {
53  
54      /** Logger */
55      private static final Log log = LogFactory.getLog(DefaultChainedWriterEngine.class);
56  
57      /** shared configuration */
58      protected ChainedFileWriterConfiguration configuration;
59  
60      /** available writers corresponding to the given configuration */
61      protected Set<ChainedFileWriter> availableWriters;
62  
63      /** selected writers obtain while the register phase. */
64      protected List<ChainedFileWriter> selectedWriters;
65  
66      private FileGrabberFromDirectory fileGrabberFromDirectory;
67  
68      private FileGrabberFromClassPath fileGrabberFromClassPath;
69  
70      public ChainedFileWriterConfiguration getConfiguration() {
71          return configuration;
72      }
73  
74      @Override
75      public List<ChainedFileWriter> getSelectedWriters() {
76          if (selectedWriters == null) {
77              checkInit("getSelectedWriters");
78              selectedWriters = new ArrayList<>();
79          }
80          return selectedWriters;
81      }
82  
83      @Override
84      public boolean containsWriter(String inputProtocol) {
85          for (ChainedFileWriter w : getSelectedWriters()) {
86              if (inputProtocol.equals(w.getInputProtocol())) {
87                  return true;
88              }
89          }
90          return false;
91      }
92  
93      @Override
94      public Set<ChainedFileWriter> getAvailableWriters() {
95          if (availableWriters == null) {
96              checkInit("getAvailableWriters");
97              availableWriters = filterWriterForModelType(
98                      getConfiguration().getWriters(),
99                      getConfiguration().getModelType()
100             );
101         }
102         return availableWriters;
103     }
104 
105     @Override
106     public void registerInclude(String include) {
107         checkInit("registerInclude");
108 
109         List<ChainedFileWriter> selectedWriters = getSelectedWriters();
110 
111         if (log.isDebugEnabled()) {
112             log.debug(">>>>>>>>>>>>>>>>> [" + include + "]");
113             log.debug("Will register include " + "[" + include + "]");
114             log.debug("actual selected writers : " + selectedWriters);
115         }
116         ModelFileWriterEntryType selectedType = null;
117         Matcher matcher = null;
118 
119         // obtain the good type of entry and the corresponding matcher
120 
121         for (ModelFileWriterEntryType type :
122                 ModelFileWriterEntryType.values()) {
123             matcher = type.getMatcher(include);
124             if (matcher != null) {
125                 // get a matcher
126                 selectedType = type;
127                 break;
128             }
129         }
130 
131         if (selectedType == null) {
132             // no writer
133             throw new IllegalArgumentException("could not find a writer for include pattern : " + include);
134         }
135 
136         // obtain the writer
137         ChainedFileWriter writer =
138                 selectedType.getWriter(this, include, matcher);
139 
140         // create the new entry
141 
142         ChainedFileWriterEntry writerEntry =
143                 selectedType.newEntry(this, include, matcher, writer);
144 
145         // register the new entry
146 
147         writer.addEntry(writerEntry);
148         if (!selectedWriters.contains(writer)) {
149             // register the writer as to be used
150             if (log.isDebugEnabled()) {
151                 log.debug("[" + include + "] Associated with writer " + writer);
152             }
153             selectedWriters.add(writer);
154         }
155 
156         ChainedFileWriterConfiguration configuration = getConfiguration();
157 
158         String modelType = configuration.getModelType();
159 
160         String outpoutProtocol = writer.getOutputProtocol(modelType);
161 
162         if (outpoutProtocol == null) {
163             // nothing more to do
164             if (log.isDebugEnabled()) {
165                 log.debug("<<<<<<<<<<<<<<<<< [" + include + "]");
166             }
167             return;
168         }
169 
170         // the writer need the includes of another writer
171 
172         if (log.isDebugEnabled()) {
173             log.debug("[" + include + "]" + " writer " +
174                       writer.getClass().getSimpleName() +
175                       " require a next writer of protocol " + outpoutProtocol);
176         }
177 
178         ChainedFileWriter nextWriter =
179                 ((AbstractChainedFileWriter) writer).getNextWriter();
180         if (nextWriter == null) {
181             // the next writer was not initialize, just have to add new entry
182 
183             nextWriter = getWriterForInputProtocol(
184                     getAvailableWriters(),
185                     outpoutProtocol,
186                     modelType
187             );
188 
189             if (nextWriter == null) {
190                 throw new IllegalArgumentException(
191                         "could not find a writer for protocole " +
192                         outpoutProtocol + " on model " + modelType);
193             }
194             // chain writer
195             ((AbstractChainedFileWriter) writer).setNextWriter(nextWriter);
196         }
197 
198         // build the next include to do
199 
200         String basedirpath = configuration.getBasedir().getAbsolutePath();
201 
202         String outputpath = writer.getOutputDirectory(
203                 configuration.getOutputDirectory(),
204                 configuration.isTestPhase()).getAbsolutePath();
205 
206         String path = outputpath.substring(basedirpath.length() + 1);
207 
208         String newInclude = outpoutProtocol + ":" + path + ":" +
209                             nextWriter.getDefaultIncludes();
210 
211         registerInclude(newInclude);
212 
213         if (log.isDebugEnabled()) {
214             log.debug("<<<<<<<<<<<<<<<<< [" + include + "]");
215         }
216     }
217 
218     @Override
219     public void clear() {
220 
221         if (selectedWriters != null) {
222             selectedWriters.clear();
223             selectedWriters = null;
224         }
225         if (availableWriters != null) {
226             for (ChainedFileWriter writer : availableWriters) {
227                 writer.clear();
228             }
229             availableWriters.clear();
230             availableWriters = null;
231         }
232         configuration = null;
233 
234         fileGrabberFromDirectory = null;
235         fileGrabberFromClassPath = null;
236 
237     }
238 
239     @Override
240     public void init(ChainedFileWriterConfiguration configuration) {
241 
242         Preconditions.checkNotNull(configuration, "Configuration can not be null");
243 
244         this.configuration = configuration;
245         fileGrabberFromDirectory = new FileGrabberFromDirectory(configuration);
246         fileGrabberFromClassPath = new FileGrabberFromClassPath(configuration);
247 
248     }
249 
250     @Override
251     public Set<ChainedFileWriter> filterWriterForModelType(
252             Map<String, ChainedFileWriter> universe, String modelType) {
253         Set<ChainedFileWriter> result = new HashSet<>();
254         for (ChainedFileWriter w : universe.values()) {
255             if (w.acceptModel(modelType)) {
256                 if (log.isDebugEnabled()) {
257                     log.debug("writer [" + w + "] accept model " + modelType);
258                 }
259                 result.add(w);
260             }
261         }
262         return result;
263     }
264 
265     @Override
266     public ChainedFileWriter getWriterForInputProtocol(
267             Set<ChainedFileWriter> universe,
268             String inputProtocol,
269             String modelType) {
270         for (ChainedFileWriter writer : universe) {
271             if (inputProtocol.equals(writer.getInputProtocol(modelType))) {
272                 return writer;
273             }
274         }
275         return null;
276     }
277 
278     @Override
279     public ChainedFileWriter getWriterForInclude(
280             Set<ChainedFileWriter> universe,
281             String include,
282             String modelType) {
283         for (ChainedFileWriter w : universe) {
284             if (w.acceptInclude(include)) {
285                 return w;
286             }
287         }
288         return null;
289     }
290 
291     @Override
292     public ChainedFileWriterData getData(ChainedFileWriter writer) throws IOException {
293 
294         checkInit("getData");
295         File outputDir = writer.getOutputDirectory(configuration.getOutputDirectory(), configuration.isTestPhase());
296 
297         if (!outputDir.exists()) {
298             if (log.isDebugEnabled()) {
299                 log.debug("[" + writer.getInputProtocol() + "] Create output directory " + outputDir);
300             }
301             boolean b = outputDir.mkdirs();
302             if (!b) {
303                 throw new IOException("Could not create directory " + outputDir);
304             }
305         }
306 
307         // split directory and classpath entries
308         Map<String, Set<String>> directoryEntries = new HashMap<>();
309         Map<String, Set<String>> classpathEntries = new HashMap<>();
310 
311         for (ChainedFileWriterEntry e : writer.getEntries()) {
312 
313             Map<String, Set<String>> currentMap;
314 
315             if (e.isUseClassPath()) {
316                 currentMap = classpathEntries;
317             } else {
318                 currentMap = directoryEntries;
319             }
320 
321             String input = e.getInputPath();
322             Set<String> includes = currentMap.get(input);
323             if (includes == null) {
324                 currentMap.put(input, includes = new LinkedHashSet<>());
325             }
326             includes.add(e.getIncludePattern());
327 
328         }
329 
330         ChainedFileWriterData result = new ChainedFileWriterData();
331         result.setFilesByRoot(new HashMap<File, List<File>>());
332         result.setResourcesByFile(new HashMap<File, List<File>>());
333         result.setOutputDirectory(outputDir);
334 
335         File extractDirectory = writer.getExtractDirectory(configuration.getExtractDirectory(),
336                                                            configuration.isTestPhase());
337 
338         // search from directory files
339         grabFiles(extractDirectory, fileGrabberFromDirectory, directoryEntries, result);
340 
341         // search class-path files
342         grabFiles(extractDirectory, fileGrabberFromClassPath, classpathEntries, result);
343 
344         return result;
345 
346     }
347 
348     protected void grabFiles(File extractDirectory, FileGrabber grabber,
349                              Map<String, Set<String>> entries,
350                              ChainedFileWriterData result) throws IOException {
351 
352         for (Map.Entry<String, Set<String>> entry : entries.entrySet()) {
353 
354             String inputDirectory = entry.getKey();
355             Set<String> includePatterns = entry.getValue();
356             grabber.addFilesToTreate(extractDirectory, inputDirectory, includePatterns, result);
357 
358         }
359 
360     }
361 
362     protected void checkInit(String method) throws IllegalStateException {
363         if (configuration == null) {
364             throw new IllegalStateException("Engine was not initialized! Can not access to " + method +
365                                             " before the init method was invoked!");
366         }
367     }
368 
369     public enum ModelFileWriterEntryType {
370 
371         ONLY_PROTOCOL_PATTERN("^([a-zA-Z]+)$") {
372             @Override
373             public ChainedFileWriter getWriter(DefaultChainedWriterEngine engine,
374                                                String include,
375                                                Matcher matcher) {
376                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
377                 Set<ChainedFileWriter> universe = engine.getAvailableWriters();
378 
379                 String protocol = matcher.group(1).toLowerCase();
380                 ChainedFileWriter writer = engine.getWriterForInputProtocol(
381                         universe,
382                         protocol,
383                         conf.getModelType()
384                 );
385                 if (writer == null) {
386                     throw new IllegalArgumentException(
387                             "could not find the writer named '" + protocol + "', use one of " + universe);
388                 }
389                 if (log.isDebugEnabled()) {
390                     log.debug("[" + include + "] " + "writer = (" + writer + ")");
391                 }
392                 return writer;
393             }
394 
395             @Override
396             public ChainedFileWriterEntry newEntry(
397                     DefaultChainedWriterEngine engine,
398                     String include,
399                     Matcher matcher,
400                     ChainedFileWriter writer) {
401                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
402                 if (log.isDebugEnabled()) {
403                     log.debug("[" + include + "] " + "detected pattern (" + name() + ")");
404                 }
405 
406                 ChainedFileWriterEntry writerEntry = new ChainedFileWriterEntry(
407                         new File(conf.getBasedir(),
408                                  conf.isTestPhase() ?
409                                  writer.getDefaultTestInputDirectory() :
410                                  writer.getDefaultInputDirectory()
411                         ).getAbsolutePath(),
412                         writer.getDefaultIncludes()
413                 );
414                 return writerEntry;
415             }
416         },
417 
418         NO_PROTOCOL_PATTERN_WITH_CLASSPATH("^classpath:([^:]+):([^:]+)$") {
419             @Override
420             public ChainedFileWriter getWriter(DefaultChainedWriterEngine engine,
421                                                String include,
422                                                Matcher matcher) {
423                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
424                 Set<ChainedFileWriter> universe = engine.getAvailableWriters();
425 
426                 String modelType = conf.getModelType();
427                 // with no protocol pattern
428                 // pattern is discover from the includes
429 
430                 // discover the writer from the given pattern
431                 ChainedFileWriter writer = engine.getWriterForInclude(universe, include, modelType);
432                 if (writer == null) {
433                     throw new IllegalArgumentException("could not find a writer for include " + include);
434                 }
435                 if (log.isDebugEnabled()) {
436                     log.debug("[" + include + "] " + "writer = (" + writer + ")");
437                 }
438                 return writer;
439             }
440 
441             @Override
442             public ChainedFileWriterEntry newEntry(DefaultChainedWriterEngine engine,
443                                                    String include,
444                                                    Matcher matcher,
445                                                    ChainedFileWriter writer) {
446 
447                 // with no protocol pattern
448                 // pattern is discover from the includes
449                 if (log.isDebugEnabled()) {
450                     log.debug("[" + include + "] " + "detected pattern (" + name() + ")");
451                 }
452 
453                 String inputPath = matcher.group(1);
454                 String includes = matcher.group(2);
455 
456                 ChainedFileWriterEntry writerEntry = new ChainedFileWriterEntry(inputPath, includes, true);
457                 return writerEntry;
458             }
459         },
460 
461         NO_PROTOCOL_PATTERN("^([^:]+):([^:]+)$") {
462             @Override
463             public ChainedFileWriter getWriter(DefaultChainedWriterEngine engine,
464                                                String include,
465                                                Matcher matcher) {
466                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
467                 Set<ChainedFileWriter> universe = engine.getAvailableWriters();
468 
469                 String modelType = conf.getModelType();
470                 // with no protocol pattern
471                 // pattern is discover from the includes
472 
473                 // discover the writer from the given pattern
474                 ChainedFileWriter writer = engine.getWriterForInclude(universe, include, modelType);
475                 if (writer == null) {
476                     throw new IllegalArgumentException("could not find a writer for include " + include);
477                 }
478                 if (log.isDebugEnabled()) {
479                     log.debug("[" + include + "] " + "writer = (" + writer + ")");
480                 }
481                 return writer;
482             }
483 
484             @Override
485             public ChainedFileWriterEntry newEntry(DefaultChainedWriterEngine engine,
486                                                    String include,
487                                                    Matcher matcher,
488                                                    ChainedFileWriter writer) {
489                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
490                 // with no protocol pattern
491                 // pattern is discover from the includes
492                 if (log.isDebugEnabled()) {
493                     log.debug("[" + include + "] " + "detected pattern (" + name() + ")");
494                 }
495 
496                 String inputPath = matcher.group(1);
497                 String includes = matcher.group(2);
498 
499                 ChainedFileWriterEntry writerEntry = new ChainedFileWriterEntry(
500                         new File(conf.getBasedir(), inputPath).getAbsolutePath(),
501                         includes
502                 );
503                 return writerEntry;
504             }
505         },
506 
507         FULL_PATTERN_WITH_CLASSPATH("^classpath:(\\w+):([^:]+):([^:]+)$") {
508             @Override
509             public ChainedFileWriter getWriter(DefaultChainedWriterEngine engine,
510                                                String include,
511                                                Matcher matcher) {
512                 // with full pattern to search in class path (protocol + directory + includes)
513                 // pattern is discover from the includes
514                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
515                 Set<ChainedFileWriter> universe = engine.getAvailableWriters();
516 
517                 String protocol = matcher.group(1).toLowerCase();
518                 ChainedFileWriter writer = engine.getWriterForInputProtocol(universe, protocol, conf.getModelType());
519 
520                 if (writer == null) {
521                     throw new IllegalArgumentException(
522                             "could not find the writer named '" + protocol + "', use one of " + universe);
523                 }
524                 if (log.isDebugEnabled()) {
525                     log.debug("[" + include + "] " + "writer = (" + writer + ")");
526                 }
527                 return writer;
528             }
529 
530             @Override
531             public ChainedFileWriterEntry newEntry(DefaultChainedWriterEngine engine,
532                                                    String include,
533                                                    Matcher matcher,
534                                                    ChainedFileWriter writer) {
535                 // with full pattern (protocol + directory + includes)
536                 // pattern is discover from the includes
537                 if (log.isDebugEnabled()) {
538                     log.debug("[" + include + "] " + "detected pattern (" + name() + ")");
539                 }
540 
541                 String inputPath = matcher.group(2);
542                 String includes = matcher.group(3);
543 
544                 //TODO tchemit 2010-09-22 Should be able to do a pattern research
545                 ChainedFileWriterEntry writerEntry = new ChainedFileWriterEntry(
546                         inputPath,
547                         includes,
548                         true
549                 );
550                 return writerEntry;
551             }
552         },
553         FULL_PATTERN("^(\\w+):([^:]+):([^:]+)$") {
554             @Override
555             public ChainedFileWriter getWriter(DefaultChainedWriterEngine engine,
556                                                String include,
557                                                Matcher matcher) {
558                 // with full pattern (protocol + directory + includes)
559                 // pattern is discover from the includes
560                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
561                 Set<ChainedFileWriter> universe = engine.getAvailableWriters();
562 
563                 String protocol = matcher.group(1).toLowerCase();
564                 ChainedFileWriter writer = engine.getWriterForInputProtocol(universe, protocol, conf.getModelType());
565 
566                 if (writer == null) {
567                     throw new IllegalArgumentException(
568                             "could not find the writer named '" + protocol + "', use one of " + universe);
569                 }
570                 if (log.isDebugEnabled()) {
571                     log.debug("[" + include + "] " + "writer = (" + writer + ")");
572                 }
573                 return writer;
574             }
575 
576             @Override
577             public ChainedFileWriterEntry newEntry(DefaultChainedWriterEngine engine,
578                                                    String include,
579                                                    Matcher matcher,
580                                                    ChainedFileWriter writer) {
581                 ChainedFileWriterConfiguration conf = engine.getConfiguration();
582                 // with full pattern (protocol + directory + includes)
583                 // pattern is discover from the includes
584                 if (log.isDebugEnabled()) {
585                     log.debug("[" + include + "] " + "detected pattern (" + name() + ")");
586                 }
587 
588                 String inputPath = matcher.group(2);
589                 String includes = matcher.group(3);
590                 ChainedFileWriterEntry writerEntry = new ChainedFileWriterEntry(
591                         new File(conf.getBasedir(), inputPath).getAbsolutePath(),
592                         includes
593                 );
594                 return writerEntry;
595             }
596         };
597 
598         private final Pattern pattern;
599 
600         ModelFileWriterEntryType(String pattern) {
601             this.pattern = Pattern.compile(pattern);
602         }
603 
604         public Pattern getPattern() {
605             return pattern;
606         }
607 
608         public Matcher getMatcher(String include) {
609             Matcher matcher = getPattern().matcher(include);
610             return matcher.matches() ? matcher : null;
611         }
612 
613         public abstract ChainedFileWriterEntry newEntry(
614                 DefaultChainedWriterEngine engine,
615                 String include,
616                 Matcher matcher, ChainedFileWriter writer);
617 
618         public abstract ChainedFileWriter getWriter(
619                 DefaultChainedWriterEngine engine,
620                 String include,
621                 Matcher matcher);
622     }
623 
624 }