View Javadoc
1   /*
2    * #%L
3    * EUGene :: EUGene
4    * %%
5    * Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric
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.java;
24  
25  import com.google.common.base.Joiner;
26  import com.google.common.collect.Lists;
27  import org.apache.commons.collections4.CollectionUtils;
28  import org.apache.commons.lang3.StringUtils;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.codehaus.plexus.component.annotations.Component;
32  import org.nuiton.eugene.GeneratorUtil;
33  import org.nuiton.eugene.Template;
34  import org.nuiton.eugene.java.extension.AnnotationsManagerExtension;
35  import org.nuiton.eugene.java.extension.ImportsManagerExtension;
36  import org.nuiton.eugene.java.extension.ObjectModelAnnotation;
37  import org.nuiton.eugene.java.extension.ObjectModelAnnotationParameter;
38  import org.nuiton.eugene.models.object.ObjectModelAttribute;
39  import org.nuiton.eugene.models.object.ObjectModelClass;
40  import org.nuiton.eugene.models.object.ObjectModelClassifier;
41  import org.nuiton.eugene.models.object.ObjectModelElement;
42  import org.nuiton.eugene.models.object.ObjectModelEnumeration;
43  import org.nuiton.eugene.models.object.ObjectModelGenerator;
44  import org.nuiton.eugene.models.object.ObjectModelInterface;
45  import org.nuiton.eugene.models.object.ObjectModelOperation;
46  import org.nuiton.eugene.models.object.ObjectModelParameter;
47  
48  import java.io.File;
49  import java.io.IOException;
50  import java.io.Writer;
51  import java.util.Arrays;
52  import java.util.Collection;
53  import java.util.Iterator;
54  import java.util.List;
55  import java.util.Set;
56  
57  
58  /*{generator option: parentheses = true}*/
59  /*{generator option: writeString = output.write}*/
60  
61  /**
62   * JavaGenerator
63   *
64   * Stupid generation of an ObjectModel with Java classes and interfaces.
65   * Use of ImportsManager to get imports for a classifier (added in model in the JavaBuilder which construct
66   * the ObjectModel).
67   *
68   * The JavaGenerator is based on a ObjectModelGenerator : Java classes are represented by ObjectModelClass, ...
69   * Created: 22 oct. 200
70   * 9
71   *
72   * @author Florian Desbois - desbois@codelutin.com
73   */
74  @Component(role = Template.class, hint = "org.nuiton.eugene.java.JavaGenerator")
75  public class JavaGenerator extends ObjectModelGenerator {
76  
77      private static final Log log = LogFactory.getLog(JavaGenerator.class);
78  
79      protected int innerLevel;
80  
81      protected String prefix;
82  
83      @Override
84      public String getFilenameForClass(ObjectModelClass clazz) {
85          return getFilenameForClassifier(clazz);
86      }
87  
88      @Override
89      public String getFilenameForInterface(ObjectModelInterface interfacez) {
90          return getFilenameForClassifier(interfacez);
91      }
92  
93      @Override
94      public String getFilenameForEnumeration(ObjectModelEnumeration enumeration) {
95          return getFilenameForClassifier(enumeration);
96      }
97  
98      @Override
99      public String getFilenameForClassifier(ObjectModelClassifier clazz) {
100         String s = clazz.getQualifiedName();
101         int index = s.indexOf("<");
102         if (index > -1) {
103             s = s.substring(0, index);
104         }
105         return s.replace('.', File.separatorChar) + ".java";
106     }
107 
108     /**
109      * Generate from all classes.
110      *
111      * @param output Writer for generating the java file
112      * @param input  Class to manage for creating an output file
113      * @throws IOException if any pb while writing file
114      */
115     @Override
116     public void generateFromClass(Writer output, ObjectModelClass input)
117             throws IOException {
118 
119         if (isVerbose()) {
120             log.info("Will generate class " + input.getQualifiedName());
121         }
122         preparePrefix(input);
123 
124         // Imports, package et documentation
125         generateHeader(output, input);
126 
127         String abstractStr = input.isAbstract() ? " abstract" : "";
128         String staticStr = input.isStatic() ? " static" : "";
129         String className = input.getName();
130 
131         String extend = "";
132         Iterator<ObjectModelClass> j = input.getSuperclasses().iterator();
133         if (j.hasNext()) {
134             ObjectModelClassifier p = j.next();
135             extend += GeneratorUtil.getSimpleName(p.getQualifiedName());
136         }
137 
138         String implement = "";
139         for (Iterator<ObjectModelInterface> i =
140              input.getInterfaces().iterator(); i.hasNext(); ) {
141             ObjectModelClassifier parentInterface = i.next();
142             String interfaceName = GeneratorUtil.getSimpleName(
143                     parentInterface.getQualifiedName());
144             implement += interfaceName;
145             if (i.hasNext()) {
146                 implement += ", ";
147             }
148         }
149         if (log.isDebugEnabled()) {
150             log.debug(className + " : super : " + extend + ", interfaces : "
151                       + implement);
152         }
153         generateAnnotations(output, input, input);
154 
155 /*{<%=prefix%>public<%=staticStr%><%=abstractStr%> class <%=className%>}*/
156 
157 /*
158  * Définition de la super classe : il ne doit y avoir qu'une
159  */
160         if (extend.length() > 0) {
161 /*{ extends <%=extend%>}*/
162         }
163 
164         if (implement.length() > 0) {
165 /*{ implements <%=implement%>}*/
166         }
167 
168 /*{ {
169 }*/
170         generateInnerClassifiers(output, input.getInnerClassifiers());
171         preparePrefix(input);
172         generateAttributes(output, input, input.getAttributes());
173         generateOperations(output, input, input.getOperations());
174 
175 /*{<%=prefix%>} //<%=className%>
176 }*/
177     }
178 
179     @Override
180     public void generateFromInterface(Writer output,
181                                       ObjectModelInterface input)
182             throws IOException {
183         if (isVerbose()) {
184             log.info("Will generate interface " + input.getQualifiedName());
185         }
186         preparePrefix(input);
187 
188         // Imports, package et documentation
189         generateHeader(output, input);
190 
191         String interfaceName = input.getName();
192 
193         String extend = "";
194         Iterator<ObjectModelInterface> j = input.getInterfaces().iterator();
195         while (j.hasNext()) {
196             ObjectModelClassifier p = j.next();
197             extend += GeneratorUtil.getSimpleName(p.getQualifiedName());
198             if (j.hasNext()) {
199                 extend += ", ";
200             }
201         }
202         generateAnnotations(output, input, input);
203 /*{<%=prefix%>public interface <%=interfaceName%>}*/
204 
205 /*
206  * Définition de la super interface : il peut y en avoir autant qu'on veut
207  */
208         if (extend.length() > 0) {
209 /*{ extends <%=extend%>}*/
210         }
211 /*{ {
212 }*/
213         generateAttributes(output, input, input.getAttributes());
214         generateOperations(output, input, input.getOperations());
215 /*{<%=prefix%>} //<%=interfaceName%>
216 }*/
217     }
218 
219     public void generateAnnotations(Writer output,
220                                     ObjectModelClassifier clazz,
221                                     ObjectModelElement element)
222             throws IOException {
223         AnnotationsManagerExtension managers = getModel().getExtension(
224                 AnnotationsManagerExtension.OBJECTMODEL_EXTENSION,
225                 AnnotationsManagerExtension.class);
226         List<ObjectModelAnnotation> annotations =
227                 managers.getAnnotations(clazz, element);
228         for (ObjectModelAnnotation annotation : annotations) {
229 //            if (!annotation.trim().startsWith("@")) {
230 //                // add @ prefix
231 //                annotation = "@" + annotation.trim();
232 //            }
233             StringBuilder annotationBuilder = new StringBuilder("@" + annotation.getType());
234             List<ObjectModelAnnotationParameter> annotationParameters = annotation.getParameters();
235             if (CollectionUtils.isNotEmpty(annotationParameters)) {
236                 annotationBuilder.append('(');
237                 List<String> params = Lists.newArrayList();
238                 for (ObjectModelAnnotationParameter annotationParameter : annotationParameters) {
239                     String paramStr = annotationParameter.getName() + " = ";
240                     Object value = annotationParameter.getValue();
241                     if (value instanceof String) {
242                         paramStr += "\"" + value + "\"";
243                     } else if (value instanceof Enum) {
244                         Enum anEnum = (Enum) value;
245 
246                         paramStr += anEnum.getClass().getSimpleName() + "." + value;
247                     } else {
248                         paramStr += value.toString();
249                     }
250                     params.add(paramStr);
251                 }
252                 Joiner.on(", ").appendTo(annotationBuilder, params);
253 
254                 annotationBuilder.append(')');
255             }
256             String annotationStr = annotationBuilder.toString();
257 
258             if (element instanceof ObjectModelOperation || element instanceof ObjectModelAttribute) {
259 /*{<%=prefix%>}*/
260                 annotationStr = "    " + annotationStr;
261             }
262 /*{<%=annotationStr%>}*/
263             if (element instanceof ObjectModelClassifier || element instanceof ObjectModelOperation || element instanceof ObjectModelAttribute) {
264 /*{
265 }*/
266             }
267         }
268     }
269 
270     @Override
271     public void generateFromEnumeration(Writer output,
272                                         ObjectModelEnumeration input) throws IOException {
273         //FIXME tchemit 20100718 I don't understand why having two methods FromEnum and FromEnumeration ?
274         generateFromEnum(output, input);
275     }
276 
277     @Override
278     public void generateFromEnum(Writer output, ObjectModelEnumeration input)
279             throws IOException {
280         if (isVerbose()) {
281             log.info("Will generate enumeration " + input.getQualifiedName());
282         }
283         preparePrefix(input);
284         generateHeader(output, input); // Imports, package et documentation
285 
286         String enumzName = input.getName();
287 
288         String extend = "";
289         Iterator<ObjectModelInterface> j = input.getInterfaces().iterator();
290         if (j.hasNext()) {
291             ObjectModelClassifier p = j.next();
292             extend += GeneratorUtil.getSimpleName(p.getQualifiedName());
293         }
294         generateAnnotations(output, input, input);
295 /*{
296 <%=prefix%>public enum <%=enumzName%>}*/
297 
298         if (extend.length() > 0) {
299 /*{ implements <%=extend%> {
300 }*/
301         } else {
302             /*{ {
303 }*/
304         }
305         // generation of literal
306         if (input.getLiterals().isEmpty()) {
307             /*{ ; }*/
308         } else {
309             Iterator<String> i = input.getLiterals().iterator();
310             while (i.hasNext()) {
311                 String literal = i.next();
312 /*{<%=prefix%>    <%=literal%><%=(i.hasNext() ? "," : ";")%>
313 }*/
314             }
315         }
316         generateAttributes(output, input, input.getAttributes());
317         generateOperations(output, input, input.getOperations());
318 /*{<%=prefix%>} //<%=enumzName%>
319 }*/
320     }
321 
322     public void generateInnerClassifiers(Writer output,
323                                          Collection<ObjectModelClassifier>
324                                                  innerClassifiers)
325             throws IOException {
326         if (innerClassifiers == null || innerClassifiers.isEmpty()) {
327             return;
328         }
329         for (ObjectModelClassifier innerClassifier : innerClassifiers) {
330             if (innerClassifier.isClass()) {
331                 generateFromClass(output, (ObjectModelClass) innerClassifier);
332                 innerLevel--;
333                 continue;
334             }
335             if (innerClassifier.isInterface()) {
336                 generateFromInterface(output, (ObjectModelInterface)
337                         innerClassifier);
338                 innerLevel--;
339                 continue;
340             }
341             if (innerClassifier.isEnum()) {
342                 generateFromEnum(output, (ObjectModelEnumeration)
343                         innerClassifier);
344                 innerLevel--;
345             }
346         }
347     }
348 
349     protected void preparePrefix(ObjectModelClassifier clazz) {
350         if (!clazz.isInner()) {
351             innerLevel = 0;
352             prefix = "";
353         } else {
354             innerLevel++;
355             char[] tmp = new char[innerLevel * 4];
356             Arrays.fill(tmp, ' ');
357             prefix = new String(tmp);
358         }
359         if (log.isDebugEnabled()) {
360             log.debug("prefix to use for classifier " +
361                       clazz.getName() + " : [" + prefix + "]");
362         }
363     }
364 
365 
366     /**
367      * Generate Header for a classifier : Package, Documentation, Imports and Classifier signature.
368      *
369      * @param output     Writer for generating the java file
370      * @param classifier Classifier for generate header
371      * @throws IOException if any pb while writing file
372      */
373     protected void generateHeader(Writer output,
374                                   ObjectModelClassifier classifier)
375             throws IOException {
376         if (classifier.isInner()) {
377             return;
378         }
379         String packageName = classifier.getPackageName();
380 /*{package <%=packageName%>;
381 
382 }*/
383         // potentiel crash si imports non defini -> IllegalArgumentException on "imports"
384         ImportsManagerExtension managers = getModel().getExtension(
385                 ImportsManagerExtension.OBJECTMODEL_EXTENSION,
386                 ImportsManagerExtension.class);
387 
388         List<String> imports = managers.getImports(classifier);
389         for (String singleImport : imports) {
390 /*{import <%=singleImport%>;
391 }*/
392         }
393         if (CollectionUtils.isNotEmpty(imports)) {
394 /*{
395 }*/
396         }
397     }
398 
399     /**
400      * Generate attributes from a collection of ObjectModelAttribute.
401      *
402      * @param output     Writer for generating the java file
403      * @param clazz      classifier in generation
404      * @param attributes Collection of ObjectModelAttribute to generate
405      * @throws IOException if any pb while writing file
406      */
407     protected void generateAttributes(Writer output, ObjectModelClassifier clazz,
408                                       Collection<ObjectModelAttribute>
409                                               attributes)
410             throws IOException {
411 
412         for (ObjectModelAttribute attr : attributes) {
413 
414 /*{
415 }*/
416             String documentation = attr.getDocumentation();
417             if (StringUtils.isNotEmpty(documentation)) {
418 /*{<%=prefix%>    /**
419 }*/
420                 String[] lines = documentation.split("\n");
421                 for (String line : lines) {
422 /*{<%=prefix%>     * <%=line%>
423 }*/
424                 }
425 /*{<%=prefix%>     *)
426 }*/
427             }
428 
429             generateAnnotations(output, clazz, attr);
430             String attrName = attr.getName();
431             String attrVisibility = attr.getVisibility();
432             String attrType = GeneratorUtil.getSimpleName(attr.getType());
433             String attrStatic = attr.isStatic() ? "static " : "";
434             String attrFinal = attr.isFinal() ? "final " : "";
435             String attrTransient = attr.isTransient() ? "transient " : "";
436             if (clazz instanceof ObjectModelInterface) {
437 
438                 //tchemit 20100507 no modifier for constants in interfaces
439                 attrStatic = "";
440                 attrFinal = "";
441                 attrTransient = "";
442                 attrVisibility = "";
443             }
444 
445             if (StringUtils.isNotEmpty(attrVisibility)) {
446                 attrVisibility += " ";
447             }
448 
449             String attrValue = StringUtils.isNotEmpty(attr.getDefaultValue()) ?
450                                // ANO#474 FD-20100408 : Don't do any simplification for
451                                // defaultValue, must be managed when the attribute is added
452                                // to the class in the Transformer.
453                                " = " + attr.getDefaultValue() : "";
454 //                " = " + GeneratorUtil.getSimpleName(attr.getDefaultValue()) : "";
455 
456 /*{<%=prefix%>    <%=attrVisibility%><%=attrStatic%><%=attrFinal%><%=attrTransient%><%=attrType%> <%=attrName%><%=attrValue%>;
457 }*/
458         }
459     }
460 
461     /**
462      * Generate operations from a collection of ObjectModelOperation
463      *
464      * @param output     Writer for generating the java file
465      * @param clazz      classifier in generation
466      * @param operations Collection of ObjectModelOperation to generate
467      * @throws IOException if any pb while writing file
468      */
469     protected void generateOperations(Writer output, ObjectModelClassifier clazz,
470                                       Collection<ObjectModelOperation>
471                                               operations) throws IOException {
472         if (!operations.isEmpty()) {
473 /*{
474 }*/
475         }
476 
477         // Ano #493 : FD-20100412
478         // Use a boolean to know if the classifier is an interface
479         // Used to avoid generating visibility not needed for interface
480         boolean interfacez =
481                 ObjectModelInterface.class.isAssignableFrom(clazz.getClass());
482 
483         for (ObjectModelOperation op : operations) {
484             String opName = op.getName();
485             if (opName == null) {
486                 generateBlock(output, clazz, op);
487                 continue;
488             }
489             generateOperationDocumentation(output, op);
490 
491             generateAnnotations(output, clazz, op);
492 
493             String opVisibility = !interfacez ? op.getVisibility() : "";
494             String opStatic = op.isStatic() ? "static " : "";
495             String opAbstract = op.isAbstract() ? "abstract " : "";
496 
497             ObjectModelParameter returnParam = op.getReturnParameter();
498             String opReturn = "";
499             if (returnParam != null) {
500                 opReturn = GeneratorUtil.getSimpleName(
501                         returnParam.getType()) + " ";
502             }
503             if (StringUtils.isNotEmpty(opVisibility)) {
504                 opVisibility += " ";
505             }
506 
507 /*{<%=prefix%>    <%=opVisibility%><%=opStatic%><%=opAbstract%><%=opReturn%><%=opName%>(}*/
508             String comma = "";
509             Collection<ObjectModelParameter> params = op.getParameters();
510             for (ObjectModelParameter param : params) {
511                 String paramName = param.getName();
512                 String paramType = GeneratorUtil.getSimpleName(param.getType());
513 /*{<%=comma%><%=paramType%> <%=paramName%>}*/
514                 comma = ", ";
515             }
516 /*{)}*/
517 
518             comma = " throws ";
519             Set<String> exceptions = op.getExceptions();
520             for (String exception : exceptions) {
521                 String exceptionName = GeneratorUtil.getSimpleName(exception);
522 /*{<%=comma%><%=exceptionName%>}*/
523                 comma = ", ";
524             }
525             // tchemit 2010-08-14 fix http://www.nuiton.org/issues/show/809
526 //            if (!op.getBodyCode().isEmpty()) {
527             if (!(clazz instanceof ObjectModelInterface) && !op.isAbstract()) {
528                 String body = op.getBodyCode() == null ? "" : op.getBodyCode();
529 /*{<%=prefix%> {<%=body%><%=prefix%>}
530 
531 }*/
532             } else {
533 /*{;
534 
535 }*/
536             }
537         }
538     }
539 
540     protected void generateOperationDocumentation(Writer output,
541                                                   ObjectModelOperation op) throws IOException {
542         String documentation = op.getDocumentation();
543         if (StringUtils.isEmpty(documentation)) {
544 
545             // no documentation for this operation
546             return;
547         }
548 /*{<%=prefix%>    /**
549 <%=prefix%>}*/
550 
551         String[] documentationLines = documentation.split("\n");
552         for (String documentationLine : documentationLines) {
553 /*{<%=prefix%>     * <%=documentationLine%>
554 }*/
555         }
556         Collection<ObjectModelParameter> params = op.getParameters();
557         for (ObjectModelParameter param : params) {
558             String paramName = param.getName();
559             String paramDocumentation = param.getDocumentation();
560             if (paramDocumentation == null) {
561                 paramDocumentation = "";
562             }
563 /*{<%=prefix%>     * @param <%=paramName%> <%=paramDocumentation%>
564 }*/
565         }
566 
567         ObjectModelParameter returnParam = op.getReturnParameter();
568         String opReturn = "";
569         if (returnParam != null) {
570             opReturn = GeneratorUtil.getSimpleName(
571                     returnParam.getType()) + " ";
572             if (!opReturn.contains("void")) {
573                 String paramDocumentation = returnParam.getDocumentation();
574                 if (paramDocumentation == null) {
575                     paramDocumentation = "";
576                 }
577 /*{<%=prefix%>     * @return <%=paramDocumentation%>
578 }*/
579             }
580         }
581         Set<String> exceptions = op.getExceptions();
582         for (String exception : exceptions) {
583             String exceptionName = GeneratorUtil.getSimpleName(exception);
584 /*{<%=prefix%>     * @throws <%=exceptionName%>
585 }*/
586         }
587 /*{<%=prefix%>     *)
588 }*/
589 
590     }
591 
592     protected void generateBlock(Writer output,
593                                  ObjectModelClassifier clazz,
594                                  ObjectModelOperation op) throws IOException {
595         String opStatic = op.isStatic() ? "static " : " ";
596         
597 /*{<%=prefix%>    <%=opStatic%>{
598 <%=prefix%>    <%=op.getBodyCode()%>
599 <%=prefix%>    }
600 
601 }*/
602     }
603 
604 }