1 package org.codehaus.modello.plugin.converters;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import javax.inject.Named;
26
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Properties;
32
33 import org.codehaus.modello.ModelloException;
34 import org.codehaus.modello.ModelloParameterConstants;
35 import org.codehaus.modello.ModelloRuntimeException;
36 import org.codehaus.modello.model.Model;
37 import org.codehaus.modello.model.ModelAssociation;
38 import org.codehaus.modello.model.ModelClass;
39 import org.codehaus.modello.model.ModelDefault;
40 import org.codehaus.modello.model.ModelField;
41 import org.codehaus.modello.model.Version;
42 import org.codehaus.modello.model.VersionDefinition;
43 import org.codehaus.modello.plugin.java.AbstractJavaModelloGenerator;
44 import org.codehaus.modello.plugin.java.javasource.JClass;
45 import org.codehaus.modello.plugin.java.javasource.JInterface;
46 import org.codehaus.modello.plugin.java.javasource.JMethod;
47 import org.codehaus.modello.plugin.java.javasource.JMethodSignature;
48 import org.codehaus.modello.plugin.java.javasource.JParameter;
49 import org.codehaus.modello.plugin.java.javasource.JSourceCode;
50 import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
51 import org.codehaus.modello.plugin.java.javasource.JType;
52 import org.codehaus.modello.plugin.java.metadata.JavaClassMetadata;
53 import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
54
55
56
57
58 @Named("converters")
59 public class ConverterGenerator extends AbstractJavaModelloGenerator {
60 public void generate(Model model, Properties parameters) throws ModelloException {
61 initialize(model, parameters);
62
63 String[] versions =
64 parameters.getProperty(ModelloParameterConstants.ALL_VERSIONS).split(",");
65
66 List<Version> allVersions = new ArrayList<Version>(versions.length);
67 for (String version : versions) {
68 allVersions.add(new Version(version));
69 }
70 Collections.sort(allVersions);
71
72 Version nextVersion = null;
73 for (Version v : allVersions) {
74 if (v.greaterThan(getGeneratedVersion())) {
75 nextVersion = v;
76 break;
77 }
78 }
79
80 try {
81
82
83 generateConverters(nextVersion);
84
85 if (nextVersion == null) {
86 generateConverterTool(allVersions);
87 }
88 } catch (IOException ex) {
89 throw new ModelloException("Exception while generating model converters.", ex);
90 }
91 }
92
93 private void generateConverters(Version toVersion) throws ModelloException, IOException {
94 Model objectModel = getModel();
95
96 Version fromVersion = getGeneratedVersion();
97 String packageName = objectModel.getDefaultPackageName(true, fromVersion) + ".convert";
98
99 Version effectiveToVersion = (toVersion == null) ? fromVersion : toVersion;
100 String jDoc = "Converts from version " + fromVersion + " (with version in package name) to version "
101 + effectiveToVersion + " (with" + (toVersion != null ? "" : "out")
102 + " version in package name) of the model.";
103
104 JInterface conversionInterface = new JInterface(packageName + ".VersionConverter");
105 initHeader(conversionInterface);
106 suppressAllWarnings(objectModel, conversionInterface);
107 conversionInterface.getJDocComment().setComment(jDoc);
108
109 JClass basicConverterClass = new JClass(packageName + ".BasicVersionConverter");
110 initHeader(basicConverterClass);
111 suppressAllWarnings(objectModel, basicConverterClass);
112 basicConverterClass.getJDocComment().setComment(jDoc);
113 basicConverterClass.addInterface(conversionInterface);
114
115 VersionDefinition versionDefinition = objectModel.getVersionDefinition();
116
117 for (ModelClass modelClass : objectModel.getClasses(fromVersion)) {
118 JavaClassMetadata javaClassMetadata = (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.ID);
119
120 if (!javaClassMetadata.isEnabled()) {
121
122 continue;
123 }
124
125
126 if (toVersion != null && !toVersion.inside(modelClass.getVersionRange())) {
127
128 continue;
129 }
130
131 String methodName = "convert" + modelClass.getName();
132 String parameterName = uncapitalise(modelClass.getName());
133 String sourceClass = getSourceClassName(modelClass, fromVersion);
134 String targetClass = modelClass.getPackageName(toVersion != null, toVersion) + "." + modelClass.getName();
135
136 if (!javaClassMetadata.isAbstract()) {
137
138
139 JMethodSignature methodSig = new JMethodSignature(methodName, new JType(targetClass));
140 methodSig.addParameter(new JParameter(new JType(sourceClass), parameterName));
141 conversionInterface.addMethod(methodSig);
142
143
144 JMethod jMethod = new JMethod(methodName, new JType(targetClass), null);
145 jMethod.addParameter(new JParameter(new JType(sourceClass), parameterName));
146 basicConverterClass.addMethod(jMethod);
147
148 JSourceCode sc = jMethod.getSourceCode();
149
150 sc.add("return " + methodName + "( " + parameterName + ", new " + targetClass + "() );");
151 }
152
153
154 JMethod jMethod = new JMethod(methodName, new JType(targetClass), null);
155 jMethod.addParameter(new JParameter(new JType(sourceClass), parameterName));
156 jMethod.addParameter(new JParameter(new JType(targetClass), "value"));
157 basicConverterClass.addMethod(jMethod);
158
159 JSourceCode sc = jMethod.getSourceCode();
160
161 sc.add("if ( " + parameterName + " == null )");
162
163 sc.add("{");
164 sc.indent();
165
166 sc.add("return null;");
167
168 sc.unindent();
169 sc.add("}");
170
171 if (modelClass.getSuperClass() != null) {
172 sc.add("// Convert super class");
173
174 sc.add("value = (" + targetClass + ") convert" + modelClass.getSuperClass() + "( " + parameterName
175 + ", value );");
176
177 sc.add("");
178 }
179
180 for (ModelField modelField : modelClass.getFields(fromVersion)) {
181 String name = capitalise(modelField.getName());
182
183 if (toVersion != null) {
184 if (versionDefinition != null && versionDefinition.isFieldType()) {
185 if (versionDefinition.getValue().equals(modelField.getName())
186 || versionDefinition.getValue().equals(modelField.getAlias())) {
187 sc.add("value.set" + name + "( \"" + toVersion + "\" );");
188 continue;
189 }
190 }
191 }
192
193
194 if (toVersion != null && !toVersion.inside(modelField.getVersionRange())) {
195
196 ModelField newField = null;
197 try {
198 newField = modelClass.getField(modelField.getName(), toVersion);
199 } catch (ModelloRuntimeException e) {
200
201 continue;
202 }
203
204 if (!newField.getType().equals(modelField.getType())) {
205
206 continue;
207 }
208 }
209
210 if (modelField instanceof ModelAssociation) {
211 ModelAssociation assoc = (ModelAssociation) modelField;
212
213 if (assoc.isManyMultiplicity()) {
214 String type = assoc.getType();
215 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
216 sc.add("{");
217
218 sc.indent();
219
220 sc.add(assoc.getType() + " list = " + assoc.getDefaultValue() + ";");
221
222 sc.add("for ( java.util.Iterator i = " + parameterName + ".get" + name
223 + "().iterator(); i.hasNext(); )");
224
225 sc.add("{");
226
227 sc.indent();
228
229 if (isClassInModel(assoc.getTo(), modelClass.getModel())) {
230 String className = getSourceClassName(assoc.getToClass(), fromVersion);
231 sc.add(className + " v = (" + className + ") i.next();");
232 } else {
233 sc.add(assoc.getTo() + " v = (" + assoc.getTo() + ") i.next();");
234 }
235
236 if (isClassInModel(assoc.getTo(), objectModel)) {
237 sc.add("list.add( convert" + assoc.getTo() + "( v ) );");
238 } else {
239 sc.add("list.add( v );");
240 }
241
242 sc.unindent();
243
244 sc.add("}");
245
246 sc.add("value.set" + name + "( list );");
247
248 sc.unindent();
249
250 sc.add("}");
251 } else {
252 sc.add("{");
253
254 sc.indent();
255
256
257 sc.add(assoc.getType() + " map = " + assoc.getDefaultValue() + ";");
258
259 sc.add("for ( java.util.Iterator i = " + parameterName + ".get" + name
260 + "().entrySet().iterator(); i.hasNext(); )");
261
262 sc.add("{");
263
264 sc.indent();
265
266 sc.add("java.util.Map.Entry entry = (java.util.Map.Entry) i.next();");
267
268 if (isClassInModel(assoc.getTo(), modelClass.getModel())) {
269 String className = getSourceClassName(assoc.getToClass(), fromVersion);
270 sc.add(className + " v = (" + className + ") entry.getValue();");
271 } else {
272 sc.add(assoc.getTo() + " v = (" + assoc.getTo() + ") entry.getValue();");
273 }
274
275 if (isClassInModel(assoc.getTo(), objectModel)) {
276 sc.add("map.put( entry.getKey(), convert" + assoc.getTo() + "( v ) );");
277 } else {
278 sc.add("map.put( entry.getKey(), v );");
279 }
280
281 sc.unindent();
282
283 sc.add("}");
284
285 sc.add("value.set" + name + "( map );");
286
287 sc.unindent();
288
289 sc.add("}");
290 }
291 } else {
292 sc.add("value.set" + name + "( convert" + assoc.getTo() + "( " + parameterName + ".get" + name
293 + "() ) );");
294 }
295 } else {
296 sc.add("// Convert field " + modelField.getName());
297
298 JavaFieldMetadata javaFieldMetadata =
299 (JavaFieldMetadata) modelField.getMetadata(JavaFieldMetadata.ID);
300 String value = parameterName + "." + getPrefix(javaFieldMetadata) + name + "()";
301 sc.add("value.set" + name + "( " + value + " );");
302 }
303 }
304
305 sc.add("");
306
307 sc.add("return value;");
308 }
309
310 try (JSourceWriter interfaceWriter = newJSourceWriter(packageName, conversionInterface.getName(true));
311 JSourceWriter classWriter = newJSourceWriter(packageName, basicConverterClass.getName(true))) {
312 conversionInterface.print(interfaceWriter);
313 basicConverterClass.print(classWriter);
314 }
315 }
316
317 private void generateConverterTool(List<Version> allVersions) throws ModelloException, IOException {
318 Model objectModel = getModel();
319 String root = objectModel.getRoot(getGeneratedVersion());
320
321 ModelClass rootClass = objectModel.getClass(root, getGeneratedVersion());
322
323 String basePackage = objectModel.getDefaultPackageName(false, null);
324 String packageName = basePackage + ".convert";
325
326 String jDoc = "Converts between the available versions of the model.";
327
328 JClass converterClass = new JClass(packageName + ".ConverterTool");
329 initHeader(converterClass);
330 suppressAllWarnings(objectModel, converterClass);
331 converterClass.getJDocComment().setComment(jDoc);
332
333 converterClass.addImport("java.io.File");
334 converterClass.addImport("java.io.IOException");
335
336 converterClass.addImport("javax.xml.stream.*");
337
338 for (Version v : allVersions) {
339 writeConvertMethod(converterClass, objectModel, basePackage, allVersions, v, rootClass);
340 }
341 writeConvertMethod(converterClass, objectModel, basePackage, allVersions, null, rootClass);
342
343 try (JSourceWriter classWriter = newJSourceWriter(packageName, converterClass.getName(true))) {
344 converterClass.print(new JSourceWriter(classWriter));
345 }
346 }
347
348 private static void writeConvertMethod(
349 JClass converterClass,
350 Model objectModel,
351 String basePackage,
352 List<Version> allVersions,
353 Version v,
354 ModelClass rootClass) {
355 String modelName = objectModel.getName();
356 String rootClassName = rootClass.getName();
357
358 String targetPackage = objectModel.getDefaultPackageName(v != null, v);
359 String targetClass = targetPackage + "." + rootClassName;
360
361 String methodName = "convertFromFile";
362 if (v != null) {
363 methodName += "_" + v.toString("v", "_");
364 }
365 JMethod method = new JMethod(methodName, new JType(targetClass), null);
366 method.addParameter(new JParameter(new JType("File"), "f"));
367 method.addException(new JClass("IOException"));
368 method.addException(new JClass("XMLStreamException"));
369 converterClass.addMethod(method);
370
371 JSourceCode sc = method.getSourceCode();
372
373 sc.add(basePackage + ".io.stax." + modelName + "StaxReaderDelegate reader = new " + basePackage + ".io.stax."
374 + modelName + "StaxReaderDelegate();");
375
376 sc.add("Object value = reader.read( f );");
377
378 String prefix = "";
379 for (Version sourceVersion : allVersions) {
380 String sourcePackage = objectModel.getDefaultPackageName(true, sourceVersion);
381 String sourceClass = sourcePackage + "." + rootClassName;
382 sc.add(prefix + "if ( value instanceof " + sourceClass + " )");
383 sc.add("{");
384 sc.indent();
385
386 boolean foundFirst = false;
387 for (Version targetVersion : allVersions) {
388 if (!foundFirst) {
389 if (targetVersion.equals(sourceVersion)) {
390 foundFirst = true;
391 } else {
392 continue;
393 }
394 }
395
396 if (targetVersion.equals(v)) {
397 break;
398 }
399
400
401 String p = objectModel.getDefaultPackageName(true, targetVersion);
402 String c = p + "." + rootClassName;
403 sc.add("value = new " + p + ".convert.BasicVersionConverter().convert" + rootClassName + "( (" + c
404 + ") value );");
405 }
406
407 sc.unindent();
408 sc.add("}");
409
410 prefix = "else ";
411
412 if (sourceVersion.equals(v)) {
413 break;
414 }
415 }
416 sc.add("else");
417 sc.add("{");
418 sc.indent();
419
420 sc.add("throw new IllegalStateException( \"Can't find converter for class '\" + value.getClass() + \"'\" );");
421
422 sc.unindent();
423 sc.add("}");
424
425 sc.add("return (" + targetClass + ") value;");
426 }
427
428 private static String getSourceClassName(ModelClass modelClass, Version generatedVersion) {
429 return modelClass.getPackageName(true, generatedVersion) + "." + modelClass.getName();
430 }
431 }