View Javadoc
1   package org.codehaus.modello.plugin.java;
2   
3   /*
4    * Copyright (c) 2004, Codehaus.org
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy of
7    * this software and associated documentation files (the "Software"), to deal in
8    * the Software without restriction, including without limitation the rights to
9    * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10   * of the Software, and to permit persons to whom the Software is furnished to do
11   * so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.text.DateFormat;
28  import java.text.ParseException;
29  import java.text.SimpleDateFormat;
30  import java.util.ArrayList;
31  import java.util.Date;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Optional;
35  import java.util.Properties;
36  
37  import org.codehaus.modello.ModelloException;
38  import org.codehaus.modello.ModelloParameterConstants;
39  import org.codehaus.modello.model.BaseElement;
40  import org.codehaus.modello.model.Model;
41  import org.codehaus.modello.model.ModelAssociation;
42  import org.codehaus.modello.model.ModelClass;
43  import org.codehaus.modello.model.ModelDefault;
44  import org.codehaus.modello.model.ModelField;
45  import org.codehaus.modello.model.ModelInterface;
46  import org.codehaus.modello.model.ModelType;
47  import org.codehaus.modello.plugin.AbstractModelloGenerator;
48  import org.codehaus.modello.plugin.java.javasource.JClass;
49  import org.codehaus.modello.plugin.java.javasource.JComment;
50  import org.codehaus.modello.plugin.java.javasource.JInterface;
51  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
52  import org.codehaus.modello.plugin.java.javasource.JStructure;
53  import org.codehaus.modello.plugin.java.metadata.JavaClassMetadata;
54  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
55  import org.codehaus.modello.plugin.java.metadata.JavaModelMetadata;
56  import org.codehaus.modello.plugin.model.ModelClassMetadata;
57  import org.codehaus.plexus.util.StringUtils;
58  
59  /**
60   * AbstractJavaModelloGenerator - similar in scope to {@link AbstractModelloGenerator} but with features that
61   * java generators can use.
62   *
63   * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
64   */
65  public abstract class AbstractJavaModelloGenerator extends AbstractModelloGenerator {
66      private Optional<Integer> javaSource;
67  
68      protected boolean domAsXpp3 = true;
69  
70      protected void initialize(Model model, Properties parameters) throws ModelloException {
71          super.initialize(model, parameters);
72  
73          javaSource = Optional.ofNullable(getParameter(parameters, ModelloParameterConstants.OUTPUT_JAVA_SOURCE, null))
74                  .map(Integer::valueOf);
75  
76          domAsXpp3 = !"false".equals(parameters.getProperty(ModelloParameterConstants.DOM_AS_XPP3));
77      }
78  
79      /**
80       * Create a new java source file writer, with configured encoding.
81       *
82       * @param packageName the package of the source file to create
83       * @param className the class of the source file to create
84       * @return a JSourceWriter with configured encoding
85       * @throws IOException
86       */
87      protected JSourceWriter newJSourceWriter(String packageName, String className) throws IOException {
88          String directory = packageName.replace('.', File.separatorChar);
89  
90          File f = new File(new File(getOutputDirectory(), directory), className + ".java");
91  
92          if (!f.getParentFile().exists()) {
93              f.getParentFile().mkdirs();
94          }
95  
96          return new JSourceWriter(newWriter(f.toPath()));
97      }
98  
99      private JComment getHeaderComment() {
100         JComment comment = new JComment();
101         comment.setComment(getHeader());
102         return comment;
103     }
104 
105     protected void initHeader(JClass clazz) {
106         clazz.setHeader(getHeaderComment());
107     }
108 
109     protected void initHeader(JInterface interfaze) {
110         interfaze.setHeader(getHeaderComment());
111     }
112 
113     protected final boolean hasJavaSourceSupport(int source) {
114         return javaSource.map(i -> i >= source).orElse(false);
115     }
116 
117     protected void suppressAllWarnings(Model objectModel, JStructure structure) {
118         JavaModelMetadata javaModelMetadata = (JavaModelMetadata) objectModel.getMetadata(JavaModelMetadata.ID);
119 
120         if (hasJavaSourceSupport(5) && javaModelMetadata.isSuppressAllWarnings()) {
121             structure.appendAnnotation("@SuppressWarnings( \"all\" )");
122         }
123     }
124 
125     protected void addModelImports(JClass jClass, BaseElement baseElem) throws ModelloException {
126         String basePackageName = null;
127         if (baseElem instanceof ModelType) {
128             basePackageName = ((ModelType) baseElem).getPackageName(isPackageWithVersion(), getGeneratedVersion());
129         }
130 
131         // import interfaces
132         for (ModelInterface modelInterface : getModel().getInterfaces(getGeneratedVersion())) {
133             addModelImport(jClass, modelInterface, basePackageName);
134         }
135 
136         // import classes
137         for (ModelClass modelClass : getClasses(getModel())) {
138             addModelImport(jClass, modelClass, basePackageName);
139         }
140     }
141 
142     protected void addModelImport(JClass jClass, ModelType modelType, String basePackageName) {
143         String packageName = modelType.getPackageName(isPackageWithVersion(), getGeneratedVersion());
144 
145         if (!packageName.equals(basePackageName)) {
146             jClass.addImport(packageName + '.' + modelType.getName());
147         }
148     }
149 
150     protected String getPrefix(JavaFieldMetadata javaFieldMetadata) {
151         return javaFieldMetadata.isBooleanGetter() ? "is" : "get";
152     }
153 
154     protected String getDefaultValue(ModelAssociation association) {
155         String value = association.getDefaultValue();
156 
157         if (hasJavaSourceSupport(5)) {
158             value = StringUtils.replaceOnce(StringUtils.replaceOnce(value, "/*", ""), "*/", "");
159         }
160 
161         return value;
162     }
163 
164     protected static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS";
165 
166     protected String getJavaDefaultValue(ModelField modelField) throws ModelloException {
167         String type = modelField.getType();
168         String value = modelField.getDefaultValue();
169 
170         if ("String".equals(type)) {
171             return '"' + escapeStringLiteral(value) + '"';
172         } else if ("char".equals(type)) {
173             return '\'' + escapeStringLiteral(value) + '\'';
174         } else if ("long".equals(type)) {
175             return value + 'L';
176         } else if ("float".equals(type)) {
177             return value + 'f';
178         } else if ("Date".equals(type)) {
179             DateFormat format = new SimpleDateFormat(DEFAULT_DATE_FORMAT, Locale.US);
180             try {
181                 Date date = format.parse(value);
182                 return "new java.util.Date( " + date.getTime() + "L )";
183             } catch (ParseException pe) {
184                 throw new ModelloException("Unparseable default date: " + value, pe);
185             }
186         } else if (value != null && value.length() > 0) {
187             boolean useJava5 = hasJavaSourceSupport(5);
188             if ("Character".equals(type) && !value.contains(type)) {
189                 return newPrimitiveWrapper(type, "'" + escapeStringLiteral(value) + "'", useJava5);
190             } else if ("Boolean".equals(type) && !value.contains(type)) {
191                 return newPrimitiveWrapper(type, value, true);
192             } else if ("Byte".equals(type) && !value.contains(type)) {
193                 return newPrimitiveWrapper(type, "(byte) " + value, useJava5);
194             } else if ("Short".equals(type) && !value.contains(type)) {
195                 return newPrimitiveWrapper(type, "(short) " + value, useJava5);
196             } else if ("Integer".equals(type) && !value.contains(type)) {
197                 return newPrimitiveWrapper(type, value, useJava5);
198             } else if ("Long".equals(type) && !value.contains(type)) {
199                 return newPrimitiveWrapper(type, value + 'L', useJava5);
200             } else if ("Float".equals(type) && !value.contains(type)) {
201                 return newPrimitiveWrapper(type, value + 'f', useJava5);
202             } else if ("Double".equals(type) && !value.contains(type)) {
203                 return newPrimitiveWrapper(type, value, useJava5);
204             }
205         }
206 
207         return value;
208     }
209 
210     private String newPrimitiveWrapper(String type, String value, boolean useJava5) {
211         if (useJava5) {
212             return type + ".valueOf( " + value + " )";
213         } else {
214             return "new " + type + "( " + value + " )";
215         }
216     }
217 
218     private String escapeStringLiteral(String str) {
219         StringBuilder buffer = new StringBuilder(str.length() + 32);
220 
221         for (int i = 0, n = str.length(); i < n; i++) {
222             char c = str.charAt(i);
223             switch (c) {
224                 case '\0':
225                     buffer.append("\\0");
226                     break;
227                 case '\t':
228                     buffer.append("\\t");
229                     break;
230                 case '\r':
231                     buffer.append("\\r");
232                     break;
233                 case '\n':
234                     buffer.append("\\n");
235                     break;
236                 case '\\':
237                     buffer.append("\\\\");
238                     break;
239                 default:
240                     buffer.append(c);
241             }
242         }
243 
244         return buffer.toString();
245     }
246 
247     protected String getValueChecker(String type, String value, ModelField field) throws ModelloException {
248         String retVal;
249         if ("boolean".equals(type)
250                 || "double".equals(type)
251                 || "float".equals(type)
252                 || "int".equals(type)
253                 || "long".equals(type)
254                 || "short".equals(type)
255                 || "byte".equals(type)
256                 || "char".equals(type)) {
257             retVal = "if ( " + value + " != " + getJavaDefaultValue(field) + " )";
258         } else if (ModelDefault.LIST.equals(type)
259                 || ModelDefault.SET.equals(type)
260                 || ModelDefault.MAP.equals(type)
261                 || ModelDefault.PROPERTIES.equals(type)) {
262             retVal = "if ( ( " + value + " != null ) && ( " + value + ".size() > 0 ) )";
263         } else if ("String".equals(type) && field.getDefaultValue() != null) {
264             retVal = "if ( ( " + value + " != null ) && !" + value + ".equals( \"" + field.getDefaultValue() + "\" ) )";
265         } else if ("Date".equals(type) && field.getDefaultValue() != null) {
266             retVal = "if ( ( " + value + " != null ) && !" + value + ".equals( " + getJavaDefaultValue(field) + " ) )";
267         } else {
268             retVal = "if ( " + value + " != null )";
269         }
270         return retVal;
271     }
272 
273     protected List<ModelClass> getClasses(Model model) {
274         List<ModelClass> modelClasses = new ArrayList<ModelClass>();
275 
276         for (ModelClass modelClass : model.getClasses(getGeneratedVersion())) {
277             if (isRelevant(modelClass)) {
278                 modelClasses.add(modelClass);
279             }
280         }
281 
282         return modelClasses;
283     }
284 
285     protected boolean isRelevant(ModelClass modelClass) {
286         return isJavaEnabled(modelClass) && !isTrackingSupport(modelClass);
287     }
288 
289     protected boolean isJavaEnabled(ModelClass modelClass) {
290         JavaClassMetadata javaClassMetadata = (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.ID);
291         return javaClassMetadata.isEnabled();
292     }
293 
294     protected boolean isTrackingSupport(ModelClass modelClass) {
295         ModelClassMetadata modelClassMetadata = (ModelClassMetadata) modelClass.getMetadata(ModelClassMetadata.ID);
296         if (StringUtils.isNotEmpty(modelClassMetadata.getLocationTracker())) {
297             return true;
298         }
299         if (StringUtils.isNotEmpty(modelClassMetadata.getSourceTracker())) {
300             return true;
301         }
302         return false;
303     }
304 }