1 package org.codehaus.modello.plugin.jackson;
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.List;
29 import java.util.Properties;
30
31 import org.codehaus.modello.ModelloException;
32 import org.codehaus.modello.model.Model;
33 import org.codehaus.modello.model.ModelAssociation;
34 import org.codehaus.modello.model.ModelClass;
35 import org.codehaus.modello.model.ModelDefault;
36 import org.codehaus.modello.model.ModelField;
37 import org.codehaus.modello.plugin.java.javasource.JClass;
38 import org.codehaus.modello.plugin.java.javasource.JConstructor;
39 import org.codehaus.modello.plugin.java.javasource.JField;
40 import org.codehaus.modello.plugin.java.javasource.JMethod;
41 import org.codehaus.modello.plugin.java.javasource.JParameter;
42 import org.codehaus.modello.plugin.java.javasource.JSourceCode;
43 import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
44 import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
45 import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
46 import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
47
48
49
50
51 @Named("jackson-writer")
52 public class JacksonWriterGenerator extends AbstractJacksonGenerator {
53
54 private boolean requiresDomSupport;
55
56 public void generate(Model model, Properties parameters) throws ModelloException {
57 initialize(model, parameters);
58
59 requiresDomSupport = true;
60
61 try {
62 generateJacksonWriter();
63 } catch (IOException ex) {
64 throw new ModelloException("Exception while generating JSON Jackson Writer.", ex);
65 }
66 }
67
68 private void generateJacksonWriter() throws ModelloException, IOException {
69 Model objectModel = getModel();
70
71 String packageName =
72 objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.jackson";
73
74 String marshallerName = getFileName("JacksonWriter");
75
76 JSourceWriter sourceWriter = newJSourceWriter(packageName, marshallerName);
77
78 JClass jClass = new JClass(packageName + '.' + marshallerName);
79 initHeader(jClass);
80
81 jClass.addImport("com.fasterxml.jackson.core.JsonFactory");
82 jClass.addImport("com.fasterxml.jackson.core.JsonGenerator");
83 jClass.addImport("com.fasterxml.jackson.core.JsonGenerator.Feature");
84 jClass.addImport("java.io.IOException");
85 jClass.addImport("java.io.OutputStream");
86 jClass.addImport("java.io.OutputStreamWriter");
87 jClass.addImport("java.io.Writer");
88
89 addModelImports(jClass, null);
90
91 JField factoryField = new JField(new JClass("JsonFactory"), "factory");
92 factoryField.getModifiers().setFinal(true);
93 factoryField.setInitString("new JsonFactory()");
94 jClass.addField(factoryField);
95
96 String root = objectModel.getRoot(getGeneratedVersion());
97
98 JConstructor jacksonWriterConstructor = new JConstructor(jClass);
99 JSourceCode sc = jacksonWriterConstructor.getSourceCode();
100 sc.add("factory.enable( Feature.AUTO_CLOSE_JSON_CONTENT );");
101 sc.add("factory.enable( Feature.AUTO_CLOSE_TARGET );");
102 sc.add("factory.enable( Feature.ESCAPE_NON_ASCII );");
103 sc.add("factory.enable( Feature.FLUSH_PASSED_TO_STREAM );");
104 sc.add("factory.enable( Feature.QUOTE_FIELD_NAMES );");
105 sc.add("factory.enable( Feature.QUOTE_NON_NUMERIC_NUMBERS );");
106 sc.add("factory.disable( Feature.WRITE_NUMBERS_AS_STRINGS );");
107
108 jClass.addConstructor(jacksonWriterConstructor);
109
110 writeAllClasses(objectModel, jClass);
111
112
113
114
115
116 if (requiresDomSupport) {
117 jClass.addImport("com.fasterxml.jackson.databind.ObjectMapper");
118
119 sc.add("factory.setCodec( new ObjectMapper() );");
120 }
121
122
123
124
125
126 JMethod marshall = new JMethod("write");
127
128 String rootElementParameterName = uncapitalise(root);
129 marshall.addParameter(new JParameter(new JClass("Writer"), "writer"));
130 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
131
132 marshall.addException(new JClass("IOException"));
133
134 sc = marshall.getSourceCode();
135
136 sc.add("JsonGenerator generator = factory.createGenerator( writer );");
137
138 sc.add("generator.useDefaultPrettyPrinter();");
139
140 sc.add("write" + root + "( " + rootElementParameterName + ", generator );");
141
142 sc.add("generator.close();");
143
144 jClass.addMethod(marshall);
145
146
147
148
149
150 marshall = new JMethod("write");
151
152 marshall.addParameter(new JParameter(new JClass("OutputStream"), "stream"));
153 marshall.addParameter(new JParameter(new JClass(root), rootElementParameterName));
154
155 marshall.addException(new JClass("IOException"));
156
157 sc = marshall.getSourceCode();
158
159 sc.add("write( new OutputStreamWriter( stream, "
160 + rootElementParameterName
161 + ".getModelEncoding() ), "
162 + rootElementParameterName
163 + " );");
164
165 jClass.addMethod(marshall);
166
167 jClass.print(sourceWriter);
168
169 sourceWriter.close();
170 }
171
172 private void writeAllClasses(Model objectModel, JClass jClass) throws ModelloException {
173 for (ModelClass clazz : getClasses(objectModel)) {
174 writeClass(clazz, jClass);
175 }
176 }
177
178 private void writeClass(ModelClass modelClass, JClass jClass) throws ModelloException {
179 String className = modelClass.getName();
180
181 String uncapClassName = uncapitalise(className);
182
183 JMethod marshall = new JMethod("write" + className);
184
185 marshall.addParameter(new JParameter(new JClass(className), uncapClassName));
186 marshall.addParameter(new JParameter(new JClass("JsonGenerator"), "generator"));
187
188 marshall.addException(new JClass("IOException"));
189
190 marshall.getModifiers().makePrivate();
191
192 JSourceCode sc = marshall.getSourceCode();
193
194 sc.add("generator.writeStartObject();");
195
196 List<ModelField> modelFields = getFieldsForXml(modelClass, getGeneratedVersion());
197
198 final boolean useJava5 = hasJavaSourceSupport(5);
199
200
201 for (ModelField field : modelFields) {
202 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
203
204 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) field.getMetadata(JavaFieldMetadata.ID);
205
206 String fieldTagName = resolveTagName(field, xmlFieldMetadata);
207
208 String type = field.getType();
209
210 String value = uncapClassName + "." + getPrefix(javaFieldMetadata) + capitalise(field.getName()) + "()";
211
212 if (field instanceof ModelAssociation) {
213 ModelAssociation association = (ModelAssociation) field;
214
215 if (association.isOneMultiplicity()) {
216 sc.add(getValueChecker(type, value, association));
217
218 sc.add("{");
219 sc.addIndented("generator.writeFieldName( \"" + fieldTagName + "\" );");
220 sc.addIndented("write" + association.getTo() + "( (" + association.getTo() + ") " + value
221 + ", generator );");
222 sc.add("}");
223 } else {
224
225
226 XmlAssociationMetadata xmlAssociationMetadata =
227 (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
228
229 type = association.getType();
230 String toType = association.getTo();
231
232 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
233 sc.add(getValueChecker(type, value, association));
234
235 sc.add("{");
236 sc.indent();
237
238 sc.add("generator.writeArrayFieldStart( \"" + fieldTagName + "\" );");
239
240 if (useJava5) {
241 sc.add("for ( " + toType + " o : " + value + " )");
242 } else {
243 sc.add("for ( java.util.Iterator it = " + value + ".iterator(); it.hasNext(); )");
244 }
245
246 sc.add("{");
247 sc.indent();
248
249 if (!useJava5) {
250 sc.add(toType + " o = (" + toType + " ) it.next();");
251 }
252
253 if (isClassInModel(association.getTo(), modelClass.getModel())) {
254 sc.add("write" + toType + "( o, generator );");
255 } else {
256 sc.add("generator.writeObject( o ); ");
257 }
258
259 sc.unindent();
260 sc.add("}");
261
262 sc.add("generator.writeEndArray();");
263
264 sc.unindent();
265 sc.add("}");
266 } else {
267
268
269 sc.add(getValueChecker(type, value, field));
270
271 sc.add("{");
272 sc.indent();
273
274 if (xmlAssociationMetadata.isMapExplode()) {
275 sc.add("generator.writeArrayFieldStart( \"" + fieldTagName + "\" );");
276 } else {
277 sc.add("generator.writeObjectFieldStart( \"" + fieldTagName + "\" );");
278 }
279
280 StringBuilder entryTypeBuilder = new StringBuilder("java.util.Map.Entry");
281
282 if (useJava5) {
283 entryTypeBuilder.append('<');
284
285 if (association.getType().equals(ModelDefault.PROPERTIES)) {
286 entryTypeBuilder.append("Object, Object");
287 } else {
288 entryTypeBuilder.append("String, ").append(association.getTo());
289 }
290
291 entryTypeBuilder.append('>');
292 }
293
294 if (useJava5) {
295 sc.add("for ( " + entryTypeBuilder + " entry : " + value + ".entrySet() )");
296 } else {
297 sc.add("for ( java.util.Iterator it = " + value
298 + ".entrySet().iterator(); it.hasNext(); )");
299 }
300
301 sc.add("{");
302 sc.indent();
303
304 if (!useJava5) {
305 sc.add(entryTypeBuilder + " entry = (" + entryTypeBuilder + ") it.next();");
306 }
307
308 sc.add("final String key = String.valueOf( entry.getKey() );");
309 sc.add("final String value = String.valueOf( entry.getValue() );");
310
311 if (xmlAssociationMetadata.isMapExplode()) {
312 sc.add("generator.writeStartObject();");
313 sc.add("generator.writeStringField( \"key\", key );");
314 sc.add("generator.writeStringField( \"value\", value );");
315 sc.add("generator.writeEndObject();");
316 } else {
317 sc.add("generator.writeStringField( key, value );");
318 }
319
320 sc.unindent();
321 sc.add("}");
322
323 if (xmlAssociationMetadata.isMapExplode()) {
324 sc.add("generator.writeEndArray();");
325 } else {
326 sc.add("generator.writeEndObject();");
327 }
328
329 sc.unindent();
330 sc.add("}");
331 }
332 }
333 } else {
334 sc.add(getValueChecker(type, value, field));
335
336 sc.add("{");
337 if ("DOM".equals(field.getType())) {
338 if (domAsXpp3) {
339 getLogger()
340 .warn("Xpp3Dom not supported for "
341 + modelClass.getName()
342 + "#"
343 + field.getName()
344 + ", it will be treated as a regular Java Object.");
345 }
346
347 requiresDomSupport = true;
348
349 sc.addIndented("generator.writeObjectField( \"" + fieldTagName + "\", " + value + " );");
350 } else {
351 sc.addIndented("generator.writeObjectField( \"" + fieldTagName + "\", " + value + " );");
352 }
353 sc.add("}");
354 }
355 }
356
357 sc.add("generator.writeEndObject();");
358
359 jClass.addMethod(marshall);
360 }
361 }