View Javadoc
1   package org.codehaus.modello.plugin.stax;
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 javax.inject.Named;
26  
27  import java.io.IOException;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.codehaus.modello.ModelloException;
33  import org.codehaus.modello.ModelloParameterConstants;
34  import org.codehaus.modello.model.Model;
35  import org.codehaus.modello.model.ModelAssociation;
36  import org.codehaus.modello.model.ModelClass;
37  import org.codehaus.modello.model.ModelDefault;
38  import org.codehaus.modello.model.ModelField;
39  import org.codehaus.modello.model.Version;
40  import org.codehaus.modello.model.VersionDefinition;
41  import org.codehaus.modello.plugin.java.javasource.JClass;
42  import org.codehaus.modello.plugin.java.javasource.JField;
43  import org.codehaus.modello.plugin.java.javasource.JMethod;
44  import org.codehaus.modello.plugin.java.javasource.JParameter;
45  import org.codehaus.modello.plugin.java.javasource.JSourceCode;
46  import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
47  import org.codehaus.modello.plugin.java.javasource.JType;
48  import org.codehaus.modello.plugin.java.metadata.JavaAssociationMetadata;
49  import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
50  import org.codehaus.modello.plugins.xml.metadata.XmlAssociationMetadata;
51  import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata;
52  import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata;
53  import org.codehaus.plexus.util.StringUtils;
54  
55  /**
56   * @author <a href="mailto:jason@modello.org">Jason van Zyl</a>
57   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
58   */
59  @Named("stax-reader")
60  public class StaxReaderGenerator extends AbstractStaxGenerator {
61  
62      private boolean requiresDomSupport;
63  
64      @Override
65      public void generate(Model model, Map<String, Object> parameters) throws ModelloException {
66          initialize(model, parameters);
67  
68          requiresDomSupport = false;
69  
70          try {
71              generateStaxReader();
72  
73              VersionDefinition versionDefinition = model.getVersionDefinition();
74              if (versionDefinition != null) {
75                  String versions = (String) parameters.get(ModelloParameterConstants.ALL_VERSIONS);
76  
77                  if (versions != null) {
78                      generateStaxReaderDelegate(Arrays.asList(versions.split(",")));
79                  }
80              }
81          } catch (IOException ex) {
82              throw new ModelloException("Exception while generating StAX Reader.", ex);
83          }
84      }
85  
86      /**
87       * Generate a StAX reader, a <code><i>ModelName</i>StaxReader</code> class in <code>io.stax</code> sub-package
88       * with <code>public <i>RootClass</i> read( ... )</code> methods.
89       *
90       * @throws ModelloException
91       * @throws IOException
92       */
93      private void generateStaxReader() throws ModelloException, IOException {
94          Model objectModel = getModel();
95  
96          String packageName =
97                  objectModel.getDefaultPackageName(isPackageWithVersion(), getGeneratedVersion()) + ".io.stax";
98  
99          String unmarshallerName = getFileName("StaxReader");
100 
101         JSourceWriter sourceWriter = newJSourceWriter(packageName, unmarshallerName);
102 
103         JClass jClass = new JClass(packageName + '.' + unmarshallerName);
104         initHeader(jClass);
105         suppressAllWarnings(objectModel, jClass);
106 
107         jClass.addImport("java.io.IOException");
108         jClass.addImport("java.io.Reader");
109         jClass.addImport("java.io.FileInputStream");
110         jClass.addImport("java.io.InputStream");
111         jClass.addImport("java.io.StringWriter");
112         jClass.addImport("java.io.StringReader");
113         jClass.addImport("java.io.ByteArrayInputStream");
114         jClass.addImport("java.io.InputStreamReader");
115         jClass.addImport("java.text.DateFormat");
116         jClass.addImport("java.text.ParsePosition");
117         jClass.addImport("java.util.regex.Matcher");
118         jClass.addImport("java.util.regex.Pattern");
119         jClass.addImport("java.util.Locale");
120         jClass.addImport("javax.xml.XMLConstants");
121         jClass.addImport("javax.xml.stream.*");
122 
123         addModelImports(jClass, null);
124 
125         // ----------------------------------------------------------------------
126         // Write reference resolvers.
127         // ----------------------------------------------------------------------
128 
129         ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
130         JClass rootType = new JClass(root.getName());
131 
132         GeneratorNode rootNode = findRequiredReferenceResolvers(root, null);
133 
134         writeReferenceResolvers(rootNode, jClass);
135         for (GeneratorNode node : rootNode.getNodesWithReferencableChildren().values()) {
136             writeReferenceResolvers(node, jClass);
137         }
138 
139         // ----------------------------------------------------------------------
140         // Write the class parsers
141         // ----------------------------------------------------------------------
142 
143         writeAllClassesParser(objectModel, jClass);
144 
145         // ----------------------------------------------------------------------
146         // Write helpers
147         // ----------------------------------------------------------------------
148 
149         writeHelpers(jClass);
150 
151         if (requiresDomSupport) {
152             writeBuildDomMethod(jClass);
153         }
154 
155         // ----------------------------------------------------------------------
156         // Write the read(XMLStreamReader,boolean) method which will do the unmarshalling.
157         // ----------------------------------------------------------------------
158 
159         JMethod unmarshall = new JMethod("read", rootType, null);
160         unmarshall.getModifiers().makePrivate();
161 
162         unmarshall.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
163         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
164 
165         unmarshall.addException(new JClass("IOException"));
166         unmarshall.addException(new JClass("XMLStreamException"));
167 
168         JSourceCode sc = unmarshall.getSourceCode();
169 
170         String tagName = resolveTagName(root);
171         String className = root.getName();
172         String variableName = uncapitalise(className);
173 
174         if (requiresDomSupport && !domAsXpp3) {
175             sc.add("if ( _doc_ == null )");
176             sc.add("{");
177             sc.indent();
178             sc.add("try");
179             sc.add("{");
180             sc.addIndented("initDoc();");
181             sc.add("}");
182             sc.add("catch ( javax.xml.parsers.ParserConfigurationException pce )");
183             sc.add("{");
184             sc.addIndented(
185                     "throw new XMLStreamException( \"Unable to create DOM document: \" + pce.getMessage(), pce );");
186             sc.add("}");
187             sc.unindent();
188             sc.add("}");
189         }
190 
191         sc.add("int eventType = xmlStreamReader.getEventType();");
192 
193         sc.add("String encoding = null;");
194 
195         sc.add("while ( eventType != XMLStreamConstants.END_DOCUMENT )");
196 
197         sc.add("{");
198         sc.indent();
199 
200         sc.add("if ( eventType == XMLStreamConstants.START_DOCUMENT )");
201         sc.add("{");
202         sc.addIndented("encoding = xmlStreamReader.getCharacterEncodingScheme();");
203         sc.add("}");
204 
205         sc.add("if ( eventType == XMLStreamConstants.START_ELEMENT )");
206 
207         sc.add("{");
208         sc.indent();
209 
210         sc.add("if ( strict && ! \"" + tagName + "\".equals( xmlStreamReader.getLocalName() ) )");
211 
212         sc.add("{");
213         sc.addIndented("throw new XMLStreamException( \"Expected root element '" + tagName + "' but "
214                 + "found '\" + xmlStreamReader.getLocalName() + \"'\", xmlStreamReader.getLocation(), null );");
215         sc.add("}");
216 
217         VersionDefinition versionDefinition = objectModel.getVersionDefinition();
218         if (versionDefinition != null && versionDefinition.isNamespaceType()) {
219             sc.add("String modelVersion = getVersionFromRootNamespace( xmlStreamReader );");
220 
221             writeModelVersionCheck(sc);
222         }
223 
224         sc.add(className + ' ' + variableName + " = parse" + root.getName() + "( xmlStreamReader, strict );");
225 
226         sc.add(variableName + ".setModelEncoding( encoding );");
227 
228         sc.add("resolveReferences( " + variableName + " );");
229 
230         sc.add("return " + variableName + ';');
231 
232         sc.unindent();
233         sc.add("}");
234 
235         sc.add("eventType = xmlStreamReader.next();");
236 
237         sc.unindent();
238         sc.add("}");
239 
240         sc.add("throw new XMLStreamException( \"Expected root element '" + tagName + "' but "
241                 + "found no element at all: invalid XML document\", xmlStreamReader.getLocation(), null );");
242 
243         jClass.addMethod(unmarshall);
244 
245         // ----------------------------------------------------------------------
246         // Write the read(Reader[,boolean]) methods which will do the unmarshalling.
247         // ----------------------------------------------------------------------
248 
249         unmarshall = new JMethod("read", rootType, null);
250 
251         unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
252         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
253 
254         unmarshall.addException(new JClass("IOException"));
255         unmarshall.addException(new JClass("XMLStreamException"));
256 
257         sc = unmarshall.getSourceCode();
258 
259         sc.add("XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader( reader );");
260 
261         sc.add("");
262 
263         sc.add("return read( xmlStreamReader, strict );");
264 
265         jClass.addMethod(unmarshall);
266 
267         // ----------------------------------------------------------------------
268 
269         unmarshall = new JMethod("read", rootType, null);
270 
271         unmarshall.addParameter(new JParameter(new JClass("Reader"), "reader"));
272 
273         unmarshall.addException(new JClass("IOException"));
274         unmarshall.addException(new JClass("XMLStreamException"));
275 
276         sc = unmarshall.getSourceCode();
277         sc.add("return read( reader, true );");
278 
279         jClass.addMethod(unmarshall);
280 
281         // ----------------------------------------------------------------------
282         // Write the read(InputStream[,boolean]) methods which will do the unmarshalling.
283         // ----------------------------------------------------------------------
284 
285         unmarshall = new JMethod("read", rootType, null);
286 
287         unmarshall.addParameter(new JParameter(new JClass("InputStream"), "stream"));
288         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
289 
290         unmarshall.addException(new JClass("IOException"));
291         unmarshall.addException(new JClass("XMLStreamException"));
292 
293         sc = unmarshall.getSourceCode();
294 
295         sc.add("XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader( stream );");
296 
297         sc.add("");
298 
299         sc.add("return read( xmlStreamReader, strict );");
300 
301         jClass.addMethod(unmarshall);
302 
303         // ----------------------------------------------------------------------
304 
305         unmarshall = new JMethod("read", rootType, null);
306 
307         unmarshall.addParameter(new JParameter(new JClass("InputStream"), "stream"));
308 
309         unmarshall.addException(new JClass("IOException"));
310         unmarshall.addException(new JClass("XMLStreamException"));
311 
312         sc = unmarshall.getSourceCode();
313         sc.add("return read( stream, true );");
314 
315         jClass.addMethod(unmarshall);
316 
317         // ----------------------------------------------------------------------
318         // Write the read(String[,boolean]) methods which will do the unmarshalling.
319         // ----------------------------------------------------------------------
320 
321         unmarshall = new JMethod("read", rootType, null);
322 
323         unmarshall.addParameter(new JParameter(new JClass("String"), "filePath"));
324 
325         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
326 
327         unmarshall.addException(new JClass("IOException"));
328         unmarshall.addException(new JClass("XMLStreamException"));
329 
330         sc = unmarshall.getSourceCode();
331 
332         sc.add("java.io.File file = new java.io.File( filePath );");
333 
334         sc.add("XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader( "
335                 + "file.toURI().toURL().toExternalForm(), new FileInputStream( file ) );");
336 
337         sc.add("");
338 
339         sc.add("return read( xmlStreamReader, strict );");
340 
341         jClass.addMethod(unmarshall);
342 
343         // ----------------------------------------------------------------------
344 
345         unmarshall = new JMethod("read", rootType, null);
346 
347         unmarshall.addParameter(new JParameter(new JClass("String"), "filePath"));
348 
349         unmarshall.addException(new JClass("IOException"));
350         unmarshall.addException(new JClass("XMLStreamException"));
351 
352         sc = unmarshall.getSourceCode();
353         sc.add("return read( filePath, true );");
354 
355         jClass.addMethod(unmarshall);
356 
357         // Determine the version. Currently, it causes the document to be reparsed, but could be made more efficient in
358         // future by buffering the read XML and piping that into any consequent read method.
359 
360         if (versionDefinition != null) {
361             writeDetermineVersionMethod(jClass, objectModel);
362         }
363 
364         // ----------------------------------------------------------------------
365         //
366         // ----------------------------------------------------------------------
367 
368         jClass.print(sourceWriter);
369 
370         sourceWriter.close();
371     }
372 
373     private void generateStaxReaderDelegate(List<String> versions) throws ModelloException, IOException {
374         Model objectModel = getModel();
375 
376         String packageName = objectModel.getDefaultPackageName(false, null) + ".io.stax";
377 
378         String unmarshallerName = getFileName("StaxReaderDelegate");
379 
380         JSourceWriter sourceWriter = newJSourceWriter(packageName, unmarshallerName);
381 
382         JClass jClass = new JClass(packageName + '.' + unmarshallerName);
383 
384         jClass.addImport("java.io.IOException");
385         jClass.addImport("java.io.Reader");
386 
387         jClass.addImport("javax.xml.stream.*");
388 
389         jClass.addImport("org.codehaus.plexus.util.IOUtil");
390         jClass.addImport("org.codehaus.plexus.util.xml.XmlStreamReader");
391 
392         JMethod method = new JMethod("read", new JClass("Object"), null);
393 
394         method.addParameter(new JParameter(new JClass("java.io.File"), "f"));
395 
396         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
397 
398         method.addException(new JClass("IOException"));
399         method.addException(new JClass("XMLStreamException"));
400 
401         jClass.addMethod(method);
402 
403         JSourceCode sc = method.getSourceCode();
404 
405         sc.add("String modelVersion;");
406         sc.add("Reader reader = new XmlStreamReader( f );");
407 
408         sc.add("try");
409         sc.add("{");
410         sc.addIndented("modelVersion = determineVersion( reader );");
411         sc.add("}");
412         sc.add("finally");
413         sc.add("{");
414         sc.addIndented("IOUtil.close( reader );");
415         sc.add("}");
416 
417         sc.add("reader = new XmlStreamReader( f );");
418         sc.add("try");
419         sc.add("{");
420         sc.indent();
421 
422         writeModelVersionHack(sc);
423 
424         String prefix = "";
425         for (String version : versions) {
426             sc.add(prefix + "if ( \"" + version + "\".equals( modelVersion ) )");
427             sc.add("{");
428             sc.addIndented("return new " + getModel().getDefaultPackageName(true, new Version(version)) + ".io.stax."
429                     + getFileName("StaxReader") + "().read( reader, strict );");
430             sc.add("}");
431 
432             prefix = "else ";
433         }
434 
435         sc.add("else");
436         sc.add("{");
437         sc.addIndented("throw new XMLStreamException( \"Document version '\" + modelVersion + \"' has no "
438                 + "corresponding reader.\" );");
439         sc.add("}");
440 
441         sc.unindent();
442         sc.add("}");
443         sc.add("finally");
444         sc.add("{");
445         sc.addIndented("IOUtil.close( reader );");
446         sc.add("}");
447 
448         // ----------------------------------------------------------------------
449 
450         method = new JMethod("read", new JClass("Object"), null);
451 
452         method.addParameter(new JParameter(new JClass("java.io.File"), "f"));
453 
454         method.addException(new JClass("IOException"));
455         method.addException(new JClass("XMLStreamException"));
456 
457         sc = method.getSourceCode();
458         sc.add("return read( f, true );");
459 
460         jClass.addMethod(method);
461 
462         writeDetermineVersionMethod(jClass, objectModel);
463 
464         jClass.print(sourceWriter);
465 
466         sourceWriter.close();
467     }
468 
469     private static void writeModelVersionHack(JSourceCode sc) {
470         sc.add("// legacy hack for pomVersion == 3");
471         sc.add("if ( \"3\".equals( modelVersion ) )");
472         sc.add("{");
473         sc.addIndented("modelVersion = \"3.0.0\";");
474         sc.add("}");
475     }
476 
477     private void writeDetermineVersionMethod(JClass jClass, Model objectModel) throws ModelloException {
478         VersionDefinition versionDefinition = objectModel.getVersionDefinition();
479 
480         JMethod method = new JMethod("determineVersion", new JClass("String"), null);
481 
482         method.addParameter(new JParameter(new JClass("Reader"), "reader"));
483 
484         method.addException(new JClass("IOException"));
485         method.addException(new JClass("XMLStreamException"));
486 
487         JSourceCode sc = method.getSourceCode();
488 
489         sc.add("XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader( reader );");
490 
491         sc.add("while ( xmlStreamReader.hasNext() )");
492 
493         sc.add("{");
494         sc.indent();
495 
496         sc.add("int eventType = xmlStreamReader.next();");
497 
498         sc.add("if ( eventType == XMLStreamConstants.START_ELEMENT )");
499 
500         sc.add("{");
501         sc.indent();
502 
503         if (versionDefinition.isNamespaceType()) {
504             XmlModelMetadata xmlModelMetadata = (XmlModelMetadata) objectModel.getMetadata(XmlModelMetadata.ID);
505 
506             String namespace = xmlModelMetadata.getNamespace();
507             if (namespace == null || !namespace.contains("${version}")) {
508                 throw new ModelloException("versionDefinition is namespace, but the model does not declare "
509                         + "xml.namespace on the model element");
510             }
511 
512             sc.add("return getVersionFromRootNamespace( xmlStreamReader );");
513 
514             writeNamespaceVersionGetMethod(namespace, jClass);
515         } else {
516             String value = versionDefinition.getValue();
517 
518             ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
519             ModelField field = root.getField(value, getGeneratedVersion());
520 
521             if (field == null) {
522                 throw new ModelloException("versionDefinition is field, but the model root element does not declare a "
523                         + "field '" + value + "'.");
524             }
525 
526             if (!"String".equals(field.getType())) {
527                 throw new ModelloException("versionDefinition is field, but the field is not of type String");
528             }
529 
530             sc.add("return getVersionFromField( xmlStreamReader );");
531 
532             writeFieldVersionGetMethod(field, jClass);
533         }
534 
535         sc.unindent();
536         sc.add("}");
537 
538         sc.unindent();
539         sc.add("}");
540 
541         sc.add("throw new XMLStreamException( \"Version not found in document\", xmlStreamReader.getLocation() );");
542 
543         jClass.addMethod(method);
544     }
545 
546     private static void writeFieldVersionGetMethod(ModelField field, JClass jClass) {
547         JMethod method = new JMethod("getVersionFromField", new JType("String"), null);
548         method.getModifiers().makePrivate();
549         method.addParameter(new JParameter(new JType("XMLStreamReader"), "xmlStreamReader"));
550         method.addException(new JClass("XMLStreamException"));
551         jClass.addMethod(method);
552 
553         JSourceCode sc = method.getSourceCode();
554 
555         XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
556         String value = xmlFieldMetadata.getTagName();
557         if (value == null) {
558             value = field.getName();
559         }
560 
561         // we are now at the root element. Search child elements for the correct tag name
562 
563         sc.add("int depth = 0;");
564 
565         sc.add("while ( depth >= 0 )");
566 
567         sc.add("{");
568         sc.indent();
569 
570         sc.add("int eventType = xmlStreamReader.next();");
571 
572         sc.add("if ( eventType == XMLStreamConstants.START_ELEMENT )");
573         sc.add("{");
574         sc.indent();
575 
576         sc.add("if ( depth == 0 && \"" + value + "\".equals( xmlStreamReader.getLocalName() ) )");
577         sc.add("{");
578         sc.addIndented("return xmlStreamReader.getElementText();");
579         sc.add("}");
580 
581         if (field.getAlias() != null) {
582             sc.add("if ( depth == 0 && \"" + field.getAlias() + "\".equals( xmlStreamReader.getLocalName() ) )");
583             sc.add("{");
584             sc.addIndented("return xmlStreamReader.getElementText();");
585             sc.add("}");
586         }
587 
588         sc.add("depth++;");
589 
590         sc.unindent();
591         sc.add("}");
592 
593         sc.add("if ( eventType == XMLStreamConstants.END_ELEMENT )");
594         sc.add("{");
595         sc.addIndented("depth--;");
596         sc.add("}");
597 
598         sc.unindent();
599         sc.add("}");
600 
601         sc.add("throw new XMLStreamException( \"Field: '" + value
602                 + "' does not exist in the document.\", xmlStreamReader.getLocation() );");
603     }
604 
605     private static void writeNamespaceVersionGetMethod(String namespace, JClass jClass) {
606         JMethod method = new JMethod("getVersionFromRootNamespace", new JType("String"), null);
607         method.getModifiers().makePrivate();
608         method.addParameter(new JParameter(new JType("XMLStreamReader"), "xmlStreamReader"));
609         method.addException(new JClass("XMLStreamException"));
610         jClass.addMethod(method);
611 
612         JSourceCode sc = method.getSourceCode();
613 
614         sc.add("String uri = xmlStreamReader.getNamespaceURI( \"\" );");
615 
616         sc.add("if ( uri == null )");
617 
618         sc.add("{");
619         sc.addIndented("throw new XMLStreamException( \"No namespace specified, but versionDefinition requires it\", "
620                 + "xmlStreamReader.getLocation() );");
621         sc.add("}");
622 
623         int index = namespace.indexOf("${version}");
624 
625         sc.add("String uriPrefix = \"" + namespace.substring(0, index) + "\";");
626         sc.add("String uriSuffix = \"" + namespace.substring(index + 10) + "\";");
627 
628         sc.add("if ( !uri.startsWith( uriPrefix ) || !uri.endsWith( uriSuffix ) )");
629 
630         sc.add("{");
631         sc.addIndented("throw new XMLStreamException( \"Namespace URI: '\" + uri + \"' does not match pattern '"
632                 + namespace + "'\", xmlStreamReader.getLocation() );");
633         sc.add("}");
634 
635         sc.add("return uri.substring( uriPrefix.length(), uri.length() - uriSuffix.length() );");
636     }
637 
638     /**
639      * Write code to parse every classes from a model.
640      *
641      * @param objectModel the model
642      * @param jClass the generated class source file
643      * @throws ModelloException
644      * @see {@link #writeClassParser(ModelClass, JClass, boolean)}
645      */
646     private void writeAllClassesParser(Model objectModel, JClass jClass) throws ModelloException {
647         ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion());
648 
649         for (ModelClass clazz : getClasses(objectModel)) {
650             writeClassParser(clazz, jClass, root.getName().equals(clazz.getName()));
651         }
652     }
653 
654     /**
655      * Write a <code>private <i>ClassName</i> parse<i>ClassName</i>( ... )</code> method to parse a class from a model.
656      *
657      * @param modelClass the model class
658      * @param jClass the generated class source file
659      * @param rootElement is this class the root from the model?
660      * @throws ModelloException
661      */
662     private void writeClassParser(ModelClass modelClass, JClass jClass, boolean rootElement) throws ModelloException {
663         String className = modelClass.getName();
664 
665         String capClassName = capitalise(className);
666 
667         String uncapClassName = uncapitalise(className);
668 
669         JMethod unmarshall = new JMethod("parse" + capClassName, new JClass(className), null);
670         unmarshall.getModifiers().makePrivate();
671 
672         unmarshall.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
673         unmarshall.addParameter(new JParameter(JType.BOOLEAN, "strict"));
674 
675         unmarshall.addException(new JClass("IOException"));
676         unmarshall.addException(new JClass("XMLStreamException"));
677 
678         JSourceCode sc = unmarshall.getSourceCode();
679 
680         sc.add(className + ' ' + uncapClassName + " = new " + className + "();");
681 
682         ModelField contentField = getContentField(modelClass.getAllFields(getGeneratedVersion(), true));
683 
684         if (contentField != null) {
685             writeAttributes(modelClass, uncapClassName, sc);
686 
687             writePrimitiveField(
688                     contentField,
689                     contentField.getType(),
690                     uncapClassName,
691                     "set" + capitalise(contentField.getName()),
692                     sc);
693         } else {
694             sc.add("java.util.Set parsed = new java.util.HashSet();");
695 
696             String instanceFieldName = getInstanceFieldName(className);
697 
698             writeAttributes(modelClass, uncapClassName, sc);
699 
700             if (isAssociationPartToClass(modelClass)) {
701                 jClass.addField(new JField(new JType("java.util.Map"), instanceFieldName));
702 
703                 sc.add("if ( " + instanceFieldName + " == null )");
704                 sc.add("{");
705                 sc.addIndented(instanceFieldName + " = new java.util.HashMap();");
706                 sc.add("}");
707 
708                 sc.add("String v = xmlStreamReader.getAttributeValue( null, \"modello.id\" );");
709                 sc.add("if ( v != null )");
710                 sc.add("{");
711                 sc.addIndented(instanceFieldName + ".put( v, " + uncapClassName + " );");
712                 sc.add("}");
713             }
714 
715             sc.add(
716                     "while ( ( strict ? xmlStreamReader.nextTag() : nextTag( xmlStreamReader ) ) == XMLStreamConstants.START_ELEMENT )");
717 
718             sc.add("{");
719             sc.indent();
720 
721             boolean addElse = false;
722 
723             // Write other fields
724 
725             for (ModelField field : modelClass.getAllFields(getGeneratedVersion(), true)) {
726                 XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
727 
728                 if (!xmlFieldMetadata.isAttribute() && !xmlFieldMetadata.isTransient()) {
729                     processField(field, xmlFieldMetadata, addElse, sc, uncapClassName, rootElement, jClass);
730 
731                     addElse = true;
732                 }
733             }
734 
735             /*
736                         if ( modelClass.getFields( getGeneratedVersion() ).size() > 0 )
737                         {
738                             sc.add( "else" );
739 
740                             sc.add( "{" );
741                             sc.addIndented( "parser.nextText();" );
742                             sc.add( "}" );
743                         }
744             */
745 
746             if (addElse) {
747                 sc.add("else");
748 
749                 sc.add("{");
750                 sc.indent();
751             }
752 
753             sc.add("checkUnknownElement( xmlStreamReader, strict );");
754 
755             if (addElse) {
756                 sc.unindent();
757                 sc.add("}");
758             }
759 
760             sc.unindent();
761             sc.add("}");
762 
763             // This must be last so that we guarantee the ID has been filled already
764             if (isAssociationPartToClass(modelClass)) {
765                 List<ModelField> identifierFields = modelClass.getIdentifierFields(getGeneratedVersion());
766 
767                 if (identifierFields.size() == 1) {
768                     ModelField field = (ModelField) identifierFields.get(0);
769 
770                     String v = uncapClassName + ".get" + capitalise(field.getName()) + "()";
771                     v = getValue(field.getType(), v, (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID));
772                     sc.add(instanceFieldName + ".put( " + v + ", " + uncapClassName + " );");
773                 }
774             }
775         }
776 
777         sc.add("return " + uncapClassName + ";");
778 
779         jClass.addMethod(unmarshall);
780     }
781 
782     private GeneratorNode findRequiredReferenceResolvers(ModelClass modelClass, GeneratorNode parent)
783             throws ModelloException {
784         String className = modelClass.getName();
785 
786         GeneratorNode value = new GeneratorNode(className, parent);
787 
788         for (ModelField field : modelClass.getAllFields(getGeneratedVersion(), true)) {
789             if (field instanceof ModelAssociation) {
790                 ModelAssociation association = (ModelAssociation) field;
791 
792                 if (isClassInModel(association.getTo(), getModel())) {
793                     ModelField referenceIdentifierField = getReferenceIdentifierField(association);
794 
795                     GeneratorNode child = null;
796                     if (referenceIdentifierField != null) {
797                         child = new GeneratorNode(association, parent);
798                         child.setReferencable(true);
799                     } else {
800                         if (!value.getChain().contains(association.getTo())) {
801                             // descend into child
802                             child = findRequiredReferenceResolvers(association.getToClass(), value);
803                             child.setAssociation(association);
804                         }
805                     }
806                     if (child != null) {
807                         value.addChild(child);
808                     }
809                 }
810             }
811         }
812 
813         // propagate the flag up
814         for (GeneratorNode child : value.getChildren()) {
815             if (child.isReferencable() || child.isReferencableChildren()) {
816                 value.setReferencableChildren(true);
817             }
818 
819             value.addNodesWithReferencableChildren(child.getNodesWithReferencableChildren());
820         }
821 
822         return value;
823     }
824 
825     private void writeReferenceResolvers(GeneratorNode node, JClass jClass) {
826         JMethod unmarshall = new JMethod("resolveReferences");
827 
828         unmarshall.addParameter(new JParameter(new JClass(node.getTo()), "value"));
829 
830         unmarshall.getModifiers().makePrivate();
831 
832         JSourceCode sc = unmarshall.getSourceCode();
833 
834         sc.add("java.util.Map refs;");
835 
836         for (GeneratorNode child : node.getChildren()) {
837             if (child.isReferencable()) {
838                 ModelAssociation association = child.getAssociation();
839                 String refFieldName = getRefFieldName(association);
840                 String to = association.getTo();
841                 String instanceFieldName = getInstanceFieldName(to);
842 
843                 sc.add("if ( " + refFieldName + " != null )");
844                 sc.add("{");
845                 sc.indent();
846 
847                 sc.add("refs = (java.util.Map) " + refFieldName + ".get( value );");
848 
849                 sc.add("if ( refs != null )");
850                 sc.add("{");
851                 sc.indent();
852 
853                 String capAssocName = capitalise(association.getName());
854                 if (association.isOneMultiplicity()) {
855                     sc.add("String id = (String) refs.get( \"" + association.getName() + "\" );");
856                     sc.add(to + " ref = (" + to + ") " + instanceFieldName + ".get( id );");
857 
858                     // Don't set if it already is, since the Java plugin generates create/break that will throw an
859                     // exception
860 
861                     sc.add("if ( ref != null && !ref.equals( value.get" + capAssocName + "() ) )");
862                     sc.add("{");
863                     sc.addIndented("value.set" + capAssocName + "( ref );");
864                     sc.add("}");
865                 } else {
866                     sc.add("for ( int i = 0; i < value.get" + capAssocName + "().size(); i++ )");
867                     sc.add("{");
868                     sc.indent();
869 
870                     sc.add("String id = (String) refs.get( \"" + association.getName() + ".\" + i );");
871                     sc.add(to + " ref = (" + to + ") " + instanceFieldName + ".get( id );");
872                     sc.add("if ( ref != null )");
873                     sc.add("{");
874                     sc.addIndented("value.get" + capAssocName + "().set( i, ref );");
875                     sc.add("}");
876 
877                     sc.unindent();
878                     sc.add("}");
879                 }
880 
881                 sc.unindent();
882                 sc.add("}");
883 
884                 sc.unindent();
885                 sc.add("}");
886             }
887 
888             if (child.isReferencableChildren()) {
889                 ModelAssociation association = child.getAssociation();
890                 if (association.isOneMultiplicity()) {
891                     sc.add("resolveReferences( value.get" + capitalise(association.getName()) + "() );");
892                 } else {
893                     sc.add("for ( java.util.Iterator i = value.get" + capitalise(association.getName())
894                             + "().iterator(); i.hasNext(); )");
895                     sc.add("{");
896                     sc.addIndented("resolveReferences( (" + association.getTo() + ") i.next() );");
897                     sc.add("}");
898                 }
899             }
900         }
901 
902         jClass.addMethod(unmarshall);
903     }
904 
905     private static String getRefFieldName(ModelAssociation association) {
906         return uncapitalise(association.getTo()) + "References";
907     }
908 
909     private static String getInstanceFieldName(String to) {
910         return uncapitalise(to) + "Instances";
911     }
912 
913     /**
914      * Add code to parse fields of a model class that are XML attributes.
915      *
916      * @param modelClass the model class
917      * @param uncapClassName
918      * @param sc the source code to add to
919      * @throws ModelloException
920      */
921     private void writeAttributes(ModelClass modelClass, String uncapClassName, JSourceCode sc) throws ModelloException {
922         for (ModelField field : modelClass.getAllFields(getGeneratedVersion(), true)) {
923             XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
924 
925             if (xmlFieldMetadata.isAttribute() && !xmlFieldMetadata.isTransient()) {
926                 writePrimitiveField(field, field.getType(), uncapClassName, "set" + capitalise(field.getName()), sc);
927             }
928         }
929     }
930 
931     /**
932      * Generate code to process a field represented as an XML element.
933      *
934      * @param field the field to process
935      * @param xmlFieldMetadata its XML metadata
936      * @param addElse add an <code>else</code> statement before generating a new <code>if</code>
937      * @param sc the method source code to add to
938      * @param objectName the object name in the source
939      * @param rootElement is the enclosing model class the root class (for model version field handling)
940      * @param jClass the generated class source file
941      * @throws ModelloException
942      */
943     private void processField(
944             ModelField field,
945             XmlFieldMetadata xmlFieldMetadata,
946             boolean addElse,
947             JSourceCode sc,
948             String objectName,
949             boolean rootElement,
950             JClass jClass)
951             throws ModelloException {
952         String fieldTagName = resolveTagName(field, xmlFieldMetadata);
953 
954         String capFieldName = capitalise(field.getName());
955 
956         String singularName = singular(field.getName());
957 
958         String alias;
959         if (StringUtils.isEmpty(field.getAlias())) {
960             alias = "null";
961         } else {
962             alias = "\"" + field.getAlias() + "\"";
963         }
964 
965         String tagComparison = (addElse ? "else " : "") + "if ( checkFieldWithDuplicate( xmlStreamReader, \""
966                 + fieldTagName + "\", " + alias + ", parsed ) )";
967 
968         if (!(field instanceof ModelAssociation)) {
969             sc.add(tagComparison);
970 
971             sc.add("{");
972             sc.indent();
973 
974             // ModelField
975             writePrimitiveField(field, field.getType(), objectName, "set" + capFieldName, sc);
976 
977             if (rootElement && field.isModelVersionField()) {
978                 sc.add("String modelVersion = " + objectName + ".get" + capFieldName + "();");
979 
980                 writeModelVersionCheck(sc);
981             }
982 
983             sc.unindent();
984             sc.add("}");
985         } else { // model association
986             ModelAssociation association = (ModelAssociation) field;
987 
988             String associationName = association.getName();
989 
990             if (association.isOneMultiplicity()) {
991                 sc.add(tagComparison);
992 
993                 sc.add("{");
994                 sc.indent();
995 
996                 ModelField referenceIdentifierField = getReferenceIdentifierField(association);
997 
998                 if (referenceIdentifierField != null) {
999                     addCodeToAddReferences(association, jClass, sc, referenceIdentifierField, objectName);
1000 
1001                     // gobble the rest of the tag
1002                     sc.add("while ( xmlStreamReader.getEventType() != XMLStreamConstants.END_ELEMENT )");
1003                     sc.add("{");
1004                     sc.addIndented("xmlStreamReader.next();");
1005                     sc.add("}");
1006                 } else {
1007                     sc.add(objectName + ".set" + capFieldName + "( parse" + association.getTo()
1008                             + "( xmlStreamReader, strict ) );");
1009                 }
1010 
1011                 sc.unindent();
1012                 sc.add("}");
1013             } else {
1014                 // MANY_MULTIPLICITY
1015 
1016                 XmlAssociationMetadata xmlAssociationMetadata =
1017                         (XmlAssociationMetadata) association.getAssociationMetadata(XmlAssociationMetadata.ID);
1018 
1019                 String valuesTagName = resolveTagName(fieldTagName, xmlAssociationMetadata);
1020 
1021                 String type = association.getType();
1022 
1023                 boolean wrappedItems = xmlAssociationMetadata.isWrappedItems();
1024 
1025                 if (ModelDefault.LIST.equals(type) || ModelDefault.SET.equals(type)) {
1026                     JavaFieldMetadata javaFieldMetadata =
1027                             (JavaFieldMetadata) association.getMetadata(JavaFieldMetadata.ID);
1028                     JavaAssociationMetadata javaAssociationMetadata =
1029                             (JavaAssociationMetadata) association.getAssociationMetadata(JavaAssociationMetadata.ID);
1030 
1031                     String adder;
1032 
1033                     if (wrappedItems) {
1034                         sc.add(tagComparison);
1035 
1036                         sc.add("{");
1037                         sc.indent();
1038 
1039                         if (javaFieldMetadata.isSetter()) {
1040                             sc.add(type + " " + associationName + " = " + association.getDefaultValue() + ";");
1041 
1042                             sc.add(objectName + ".set" + capFieldName + "( " + associationName + " );");
1043 
1044                             adder = associationName + ".add";
1045                         } else {
1046                             adder = objectName + ".add" + association.getTo();
1047                         }
1048 
1049                         sc.add("while ( xmlStreamReader.nextTag() == XMLStreamConstants.START_ELEMENT )");
1050 
1051                         sc.add("{");
1052                         sc.indent();
1053 
1054                         sc.add("if ( \"" + valuesTagName + "\".equals( xmlStreamReader.getLocalName() ) )");
1055 
1056                         sc.add("{");
1057                         sc.indent();
1058                     } else {
1059                         sc.add((addElse ? "else " : "") + "if ( \"" + valuesTagName
1060                                 + "\".equals( xmlStreamReader.getLocalName() ) )");
1061 
1062                         sc.add("{");
1063                         sc.indent();
1064 
1065                         if (javaFieldMetadata.isGetter() && javaFieldMetadata.isSetter()) {
1066                             sc.add(type + " " + associationName + " = " + objectName + ".get" + capFieldName + "();");
1067 
1068                             sc.add("if ( " + associationName + " == null )");
1069 
1070                             sc.add("{");
1071                             sc.indent();
1072 
1073                             sc.add(associationName + " = " + association.getDefaultValue() + ";");
1074 
1075                             sc.add(objectName + ".set" + capFieldName + "( " + associationName + " );");
1076 
1077                             sc.unindent();
1078                             sc.add("}");
1079 
1080                             adder = associationName + ".add";
1081                         } else {
1082                             adder = objectName + ".add" + association.getTo();
1083                         }
1084                     }
1085 
1086                     if (isClassInModel(
1087                             association.getTo(), field.getModelClass().getModel())) {
1088                         ModelField referenceIdentifierField = getReferenceIdentifierField(association);
1089 
1090                         if (referenceIdentifierField != null) {
1091                             addCodeToAddReferences(association, jClass, sc, referenceIdentifierField, objectName);
1092                         }
1093 
1094                         if (association.getTo().equals(field.getModelClass().getName())
1095                                 || !javaAssociationMetadata.isAdder()) {
1096                             // HACK: the addXXX method will cause an OOME when compiling a self-referencing class, so we
1097                             //  just add it to the array. This could disrupt the links if you are using break/create
1098                             //  constraints in modello.
1099                             // MODELLO-273 update: Use addXXX only if no other methods are available!
1100                             sc.add(adder + "( parse" + association.getTo() + "( xmlStreamReader, strict ) );");
1101                         } else {
1102                             sc.add(objectName + ".add" + capitalise(singular(associationName)) + "( parse"
1103                                     + association.getTo() + "( xmlStreamReader, strict ) );");
1104                         }
1105                     } else {
1106                         writePrimitiveField(association, association.getTo(), associationName, "add", sc);
1107                     }
1108 
1109                     if (wrappedItems) {
1110                         sc.unindent();
1111                         sc.add("}");
1112 
1113                         sc.add("else");
1114 
1115                         sc.add("{");
1116                         sc.addIndented("throw new XMLStreamException( \"Unrecognised tag: '\" + "
1117                                 + "xmlStreamReader.getLocalName() + \"'\", xmlStreamReader.getLocation() );");
1118                         sc.add("}");
1119 
1120                         sc.unindent();
1121                         sc.add("}");
1122 
1123                         sc.unindent();
1124                         sc.add("}");
1125                     } else {
1126 
1127                         sc.unindent();
1128                         sc.add("}");
1129                     }
1130                 } else {
1131                     // Map or Properties
1132 
1133                     sc.add(tagComparison);
1134 
1135                     sc.add("{");
1136                     sc.indent();
1137 
1138                     if (xmlAssociationMetadata.isMapExplode()) {
1139                         sc.add("while ( xmlStreamReader.nextTag() == XMLStreamConstants.START_ELEMENT )");
1140 
1141                         sc.add("{");
1142                         sc.indent();
1143 
1144                         sc.add("if ( \"" + valuesTagName + "\".equals( xmlStreamReader.getLocalName() ) )");
1145 
1146                         sc.add("{");
1147                         sc.indent();
1148 
1149                         sc.add("String key = null;");
1150 
1151                         sc.add("String value = null;");
1152 
1153                         sc.add("// " + xmlAssociationMetadata.getMapStyle() + " mode.");
1154 
1155                         sc.add("while ( xmlStreamReader.nextTag() == XMLStreamConstants.START_ELEMENT )");
1156 
1157                         sc.add("{");
1158                         sc.indent();
1159 
1160                         sc.add("if ( \"key\".equals( xmlStreamReader.getLocalName() ) )");
1161 
1162                         sc.add("{");
1163                         sc.addIndented("key = xmlStreamReader.getElementText();");
1164                         sc.add("}");
1165 
1166                         sc.add("else if ( \"value\".equals( xmlStreamReader.getLocalName() ) )");
1167 
1168                         sc.add("{");
1169                         sc.addIndented("value = xmlStreamReader.getElementText()"
1170                                 + (xmlFieldMetadata.isTrim() ? ".trim()" : "") + ";");
1171                         sc.add("}");
1172 
1173                         sc.add("else");
1174 
1175                         sc.add("{");
1176                         sc.addIndented("xmlStreamReader.getText();");
1177                         sc.add("}");
1178 
1179                         sc.unindent();
1180                         sc.add("}");
1181 
1182                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
1183 
1184                         sc.unindent();
1185                         sc.add("}");
1186 
1187                         sc.add("xmlStreamReader.next();");
1188 
1189                         sc.unindent();
1190                         sc.add("}");
1191                     } else {
1192                         // INLINE Mode
1193 
1194                         sc.add("while ( xmlStreamReader.nextTag() == XMLStreamConstants.START_ELEMENT )");
1195 
1196                         sc.add("{");
1197                         sc.indent();
1198 
1199                         sc.add("String key = xmlStreamReader.getLocalName();");
1200 
1201                         sc.add("String value = xmlStreamReader.getElementText()"
1202                                 + (xmlFieldMetadata.isTrim() ? ".trim()" : "") + ";");
1203 
1204                         sc.add(objectName + ".add" + capitalise(singularName) + "( key, value );");
1205 
1206                         sc.unindent();
1207                         sc.add("}");
1208                     }
1209 
1210                     sc.unindent();
1211                     sc.add("}");
1212                 }
1213             }
1214         }
1215     }
1216 
1217     private static void addCodeToAddReferences(
1218             ModelAssociation association,
1219             JClass jClass,
1220             JSourceCode sc,
1221             ModelField referenceIdentifierField,
1222             String referredFromClass) {
1223         String refFieldName = getRefFieldName(association);
1224         if (jClass.getField(refFieldName) == null) {
1225             jClass.addField(new JField(new JType("java.util.Map"), refFieldName));
1226         }
1227 
1228         sc.add("String value = xmlStreamReader.getAttributeValue( null, \"" + referenceIdentifierField.getName()
1229                 + "\" );");
1230         sc.add("if ( value != null )");
1231         sc.add("{");
1232         sc.indent();
1233 
1234         sc.add("// This is a reference to an element elsewhere in the model");
1235         sc.add("if ( " + refFieldName + " == null )");
1236         sc.add("{");
1237         sc.addIndented(refFieldName + " = new java.util.HashMap();");
1238         sc.add("}");
1239 
1240         sc.add("java.util.Map refs = (java.util.Map) " + refFieldName + ".get( " + referredFromClass + " );");
1241         sc.add("if ( refs == null )");
1242         sc.add("{");
1243         sc.indent();
1244 
1245         sc.add("refs = new java.util.HashMap();");
1246         sc.add(refFieldName + ".put( " + referredFromClass + ", refs );");
1247 
1248         sc.unindent();
1249         sc.add("}");
1250 
1251         if (association.isOneMultiplicity()) {
1252             sc.add("refs.put( \"" + association.getName() + "\", value );");
1253         } else {
1254             sc.add("refs.put( \"" + association.getName() + ".\" + " + association.getName() + ".size(), value );");
1255         }
1256 
1257         sc.unindent();
1258         sc.add("}");
1259     }
1260 
1261     private void writeModelVersionCheck(JSourceCode sc) {
1262         writeModelVersionHack(sc);
1263 
1264         sc.add("if ( !\"" + getGeneratedVersion() + "\".equals( modelVersion ) )");
1265         sc.add("{");
1266         sc.addIndented(
1267                 "throw new XMLStreamException( \"Document model version of '\" + modelVersion + \"' doesn't match reader "
1268                         + "version of '" + getGeneratedVersion() + "'\", xmlStreamReader.getLocation() );");
1269         sc.add("}");
1270     }
1271 
1272     /**
1273      * Write code to set a primitive field with a value got from the parser, with appropriate default value, trimming
1274      * and required check logic.
1275      *
1276      * @param field the model field to set (either XML attribute or element)
1277      * @param type the type of the value read from XML
1278      * @param objectName the object name in source
1279      * @param setterName the setter method name
1280      * @param sc the source code to add to
1281      */
1282     private void writePrimitiveField(
1283             ModelField field, String type, String objectName, String setterName, JSourceCode sc) {
1284         XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) field.getMetadata(XmlFieldMetadata.ID);
1285 
1286         String tagName = resolveTagName(field, xmlFieldMetadata);
1287 
1288         String parserGetter;
1289         if (xmlFieldMetadata.isAttribute()) {
1290             parserGetter = "xmlStreamReader.getAttributeValue( null, \"" + tagName + "\" )";
1291         } else {
1292             parserGetter = "xmlStreamReader.getElementText()";
1293         }
1294 
1295         /* TODO:
1296                 if ( xmlFieldMetadata.isRequired() )
1297                 {
1298                     parserGetter = "getRequiredAttributeValue( " + parserGetter + ", \"" + tagName + "\", parser, strict )";
1299                 }
1300         */
1301         if (field.getDefaultValue() != null) {
1302             parserGetter = "getDefaultValue( " + parserGetter + ", \"" + field.getDefaultValue() + "\" )";
1303         }
1304 
1305         if (xmlFieldMetadata.isTrim()) {
1306             parserGetter = "getTrimmedValue( " + parserGetter + " )";
1307         }
1308 
1309         if ("boolean".equals(type) || "Boolean".equals(type)) {
1310             sc.add(objectName + "." + setterName + "( getBooleanValue( " + parserGetter + ", \"" + tagName
1311                     + "\", xmlStreamReader ) );");
1312         } else if ("char".equals(type)) {
1313             sc.add(objectName + "." + setterName + "( getCharacterValue( " + parserGetter + ", \"" + tagName
1314                     + "\", xmlStreamReader ) );");
1315         } else if ("double".equals(type)) {
1316             sc.add(objectName + "." + setterName + "( getDoubleValue( " + parserGetter + ", \"" + tagName
1317                     + "\", xmlStreamReader, strict ) );");
1318         } else if ("float".equals(type)) {
1319             sc.add(objectName + "." + setterName + "( getFloatValue( " + parserGetter + ", \"" + tagName
1320                     + "\", xmlStreamReader, strict ) );");
1321         } else if ("int".equals(type)) {
1322             sc.add(objectName + "." + setterName + "( getIntegerValue( " + parserGetter + ", \"" + tagName
1323                     + "\", xmlStreamReader, strict ) );");
1324         } else if ("long".equals(type)) {
1325             sc.add(objectName + "." + setterName + "( getLongValue( " + parserGetter + ", \"" + tagName
1326                     + "\", xmlStreamReader, strict ) );");
1327         } else if ("short".equals(type)) {
1328             sc.add(objectName + "." + setterName + "( getShortValue( " + parserGetter + ", \"" + tagName
1329                     + "\", xmlStreamReader, strict ) );");
1330         } else if ("byte".equals(type)) {
1331             sc.add(objectName + "." + setterName + "( getByteValue( " + parserGetter + ", \"" + tagName
1332                     + "\", xmlStreamReader, strict ) );");
1333         } else if ("String".equals(type)) {
1334             // TODO: other Primitive types
1335             sc.add(objectName + "." + setterName + "( " + parserGetter + " );");
1336         } else if ("Date".equals(type)) {
1337             sc.add("String dateFormat = "
1338                     + (xmlFieldMetadata.getFormat() != null ? "\"" + xmlFieldMetadata.getFormat() + "\"" : "null")
1339                     + ";");
1340             sc.add(objectName + "." + setterName + "( getDateValue( " + parserGetter + ", \"" + tagName
1341                     + "\", dateFormat, xmlStreamReader ) );");
1342         } else if ("DOM".equals(type)) {
1343             sc.add(objectName + "." + setterName + "( buildDom( xmlStreamReader, " + xmlFieldMetadata.isTrim()
1344                     + " ) );");
1345 
1346             requiresDomSupport = true;
1347         } else {
1348             throw new IllegalArgumentException("Unknown type: " + type);
1349         }
1350     }
1351 
1352     private void writeBuildDomMethod(JClass jClass) {
1353         if (domAsXpp3) {
1354             jClass.addImport("org.codehaus.plexus.util.xml.Xpp3Dom");
1355         } else {
1356             jClass.addField(new JField(new JClass("org.w3c.dom.Document"), "_doc_"));
1357             JMethod method = new JMethod("initDoc", null, null);
1358             method.getModifiers().makePrivate();
1359             method.addException(new JClass("javax.xml.parsers.ParserConfigurationException"));
1360 
1361             JSourceCode sc = method.getSourceCode();
1362             sc.add(
1363                     "javax.xml.parsers.DocumentBuilderFactory dbfac = javax.xml.parsers.DocumentBuilderFactory.newInstance();");
1364             sc.add("javax.xml.parsers.DocumentBuilder docBuilder = dbfac.newDocumentBuilder();");
1365             sc.add("_doc_ = docBuilder.newDocument();");
1366             jClass.addMethod(method);
1367         }
1368         String type = domAsXpp3 ? "Xpp3Dom" : "org.w3c.dom.Element";
1369         JMethod method = new JMethod("buildDom", new JType(type), null);
1370         method.getModifiers().makePrivate();
1371         method.addParameter(new JParameter(new JType("XMLStreamReader"), "xmlStreamReader"));
1372         method.addParameter(new JParameter(JType.BOOLEAN, "trim"));
1373         method.addException(new JClass("XMLStreamException"));
1374 
1375         JSourceCode sc = method.getSourceCode();
1376 
1377         sc.add("java.util.Stack elements = new java.util.Stack();");
1378 
1379         sc.add("java.util.Stack values = new java.util.Stack();");
1380 
1381         sc.add("int eventType = xmlStreamReader.getEventType();");
1382 
1383         sc.add("boolean spacePreserve = false;");
1384 
1385         sc.add("while ( xmlStreamReader.hasNext() )");
1386         sc.add("{");
1387         sc.indent();
1388 
1389         sc.add("if ( eventType == XMLStreamConstants.START_ELEMENT )");
1390         sc.add("{");
1391         sc.indent();
1392         sc.add("spacePreserve = false;");
1393         sc.add("String rawName = xmlStreamReader.getLocalName();");
1394 
1395         if (domAsXpp3) {
1396             sc.add("Xpp3Dom element = new Xpp3Dom( rawName );");
1397         } else {
1398             sc.add("org.w3c.dom.Element element = _doc_.createElement( rawName );");
1399         }
1400 
1401         sc.add("if ( !elements.empty() )");
1402         sc.add("{");
1403         sc.indent();
1404         sc.add(type + " parent = (" + type + ") elements.peek();");
1405 
1406         sc.add("parent." + (domAsXpp3 ? "addChild" : "appendChild") + "( element );");
1407         sc.unindent();
1408         sc.add("}");
1409 
1410         sc.add("elements.push( element );");
1411 
1412         sc.add("if ( xmlStreamReader.isEndElement() )");
1413         sc.add("{");
1414         sc.addIndented("values.push( null );");
1415         sc.add("}");
1416         sc.add("else");
1417         sc.add("{");
1418         sc.addIndented("values.push( new StringBuilder() );");
1419         sc.add("}");
1420 
1421         sc.add("int attributesSize = xmlStreamReader.getAttributeCount();");
1422 
1423         sc.add("for ( int i = 0; i < attributesSize; i++ )");
1424         sc.add("{");
1425         sc.indent();
1426         sc.add("String prefix = xmlStreamReader.getAttributePrefix( i );");
1427         sc.add("String localName = xmlStreamReader.getAttributeLocalName( i );");
1428         sc.add("String value = xmlStreamReader.getAttributeValue( i );");
1429         sc.add("if ( XMLConstants.DEFAULT_NS_PREFIX.equals( xmlStreamReader.getAttributeName( i ).getPrefix() ) )");
1430         sc.add("{");
1431         sc.addIndented("element.setAttribute( localName, value );");
1432         sc.add("}");
1433         sc.add("else");
1434         sc.add("{");
1435         sc.addIndented("element.setAttribute( prefix + ':'+ localName , value );");
1436         sc.add("}");
1437         sc.add(
1438                 "spacePreserve = spacePreserve || ( \"xml\".equals( prefix ) && \"space\".equals( localName ) && \"preserve\".equals( value ) );");
1439         sc.unindent();
1440         sc.add("}");
1441         sc.unindent();
1442         sc.add("}");
1443         sc.add("else if ( eventType == XMLStreamConstants.CHARACTERS )");
1444         sc.add("{");
1445         sc.indent();
1446         sc.add("StringBuilder valueBuffer = (StringBuilder) values.peek();");
1447 
1448         sc.add("String text = xmlStreamReader.getText();");
1449 
1450         sc.add("if ( trim && !spacePreserve )");
1451         sc.add("{");
1452         sc.addIndented("text = text.trim();");
1453         sc.add("}");
1454 
1455         sc.add("valueBuffer.append( text );");
1456         sc.unindent();
1457         sc.add("}");
1458         sc.add("else if ( eventType == XMLStreamConstants.END_ELEMENT )");
1459         sc.add("{");
1460         sc.indent();
1461 
1462         sc.add(type + " element = (" + type + ") elements.pop();");
1463 
1464         sc.add("// this Object could be null if it is a singleton tag");
1465         sc.add("Object accumulatedValue = values.pop();");
1466 
1467         sc.add("if ( " + (domAsXpp3 ? "element.getChildCount() == 0" : "!element.hasChildNodes()") + " )");
1468         sc.add("{");
1469         sc.addIndented("element." + (domAsXpp3 ? "setValue" : "setTextContent")
1470                 + "( ( accumulatedValue == null ) ? null : accumulatedValue.toString() );");
1471         sc.add("}");
1472 
1473         sc.add("if ( values.empty() )");
1474         sc.add("{");
1475         sc.addIndented("return element;");
1476         sc.add("}");
1477         sc.unindent();
1478         sc.add("}");
1479 
1480         sc.add("eventType = xmlStreamReader.next();");
1481 
1482         sc.unindent();
1483         sc.add("}");
1484 
1485         sc.add("throw new IllegalStateException( \"End of document found before returning to 0 depth\" );");
1486 
1487         jClass.addMethod(method);
1488     }
1489 
1490     private void writeHelpers(JClass jClass) {
1491         JMethod method = new JMethod("getTrimmedValue", new JClass("String"), null);
1492         method.getModifiers().makePrivate();
1493 
1494         method.addParameter(new JParameter(new JClass("String"), "s"));
1495 
1496         JSourceCode sc = method.getSourceCode();
1497 
1498         sc.add("if ( s != null )");
1499 
1500         sc.add("{");
1501         sc.addIndented("s = s.trim();");
1502         sc.add("}");
1503 
1504         sc.add("return s;");
1505 
1506         jClass.addMethod(method);
1507 
1508         // --------------------------------------------------------------------
1509 
1510         method = new JMethod("getDefaultValue", new JClass("String"), null);
1511         method.getModifiers().makePrivate();
1512 
1513         method.addParameter(new JParameter(new JClass("String"), "s"));
1514         method.addParameter(new JParameter(new JClass("String"), "v"));
1515 
1516         sc = method.getSourceCode();
1517 
1518         sc.add("if ( s == null )");
1519 
1520         sc.add("{");
1521         sc.addIndented("s = v;");
1522         sc.add("}");
1523 
1524         sc.add("return s;");
1525 
1526         jClass.addMethod(method);
1527 
1528         // --------------------------------------------------------------------
1529 
1530         method = new JMethod("getRequiredAttributeValue", new JClass("String"), null);
1531         method.addException(new JClass("XMLStreamException"));
1532         method.getModifiers().makePrivate();
1533 
1534         method.addParameter(new JParameter(new JClass("String"), "s"));
1535         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1536         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1537         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
1538 
1539         sc = method.getSourceCode();
1540 
1541         sc.add("if ( s == null )");
1542 
1543         sc.add("{");
1544         sc.indent();
1545 
1546         sc.add("if ( strict )");
1547 
1548         sc.add("{");
1549         sc.addIndented("throw new XMLStreamException( \"Missing required value for attribute '\" + attribute + \"'\", "
1550                 + "xmlStreamReader.getLocation() );");
1551         sc.add("}");
1552 
1553         sc.unindent();
1554         sc.add("}");
1555 
1556         sc.add("return s;");
1557 
1558         jClass.addMethod(method);
1559 
1560         // --------------------------------------------------------------------
1561 
1562         method = new JMethod("getBooleanValue", JType.BOOLEAN, null);
1563         method.addException(new JClass("XMLStreamException"));
1564         method.getModifiers().makePrivate();
1565 
1566         method.addParameter(new JParameter(new JClass("String"), "s"));
1567         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1568         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1569 
1570         sc = method.getSourceCode();
1571 
1572         sc.add("if ( s != null )");
1573 
1574         sc.add("{");
1575         sc.addIndented("return Boolean.valueOf( s ).booleanValue();");
1576         sc.add("}");
1577 
1578         sc.add("return false;");
1579 
1580         jClass.addMethod(method);
1581 
1582         // --------------------------------------------------------------------
1583 
1584         method = new JMethod("getCharacterValue", JType.CHAR, null);
1585         method.addException(new JClass("XMLStreamException"));
1586         method.getModifiers().makePrivate();
1587 
1588         method.addParameter(new JParameter(new JClass("String"), "s"));
1589         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1590         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1591 
1592         sc = method.getSourceCode();
1593 
1594         sc.add("if ( s != null )");
1595 
1596         sc.add("{");
1597         sc.addIndented("return s.charAt( 0 );");
1598         sc.add("}");
1599 
1600         sc.add("return 0;");
1601 
1602         jClass.addMethod(method);
1603 
1604         // --------------------------------------------------------------------
1605 
1606         method = convertNumericalType("getIntegerValue", JType.INT, "Integer.valueOf( s ).intValue()", "an integer");
1607 
1608         jClass.addMethod(method);
1609 
1610         // --------------------------------------------------------------------
1611 
1612         method = convertNumericalType(
1613                 "getShortValue", JType.SHORT, "Short.valueOf( s ).shortValue()", "a short integer");
1614 
1615         jClass.addMethod(method);
1616 
1617         // --------------------------------------------------------------------
1618 
1619         method = convertNumericalType("getByteValue", JType.BYTE, "Byte.valueOf( s ).byteValue()", "a byte");
1620 
1621         jClass.addMethod(method);
1622 
1623         // --------------------------------------------------------------------
1624 
1625         method = convertNumericalType("getLongValue", JType.LONG, "Long.valueOf( s ).longValue()", "a long integer");
1626 
1627         jClass.addMethod(method);
1628 
1629         // --------------------------------------------------------------------
1630 
1631         method = convertNumericalType(
1632                 "getFloatValue", JType.FLOAT, "Float.valueOf( s ).floatValue()", "a floating point number");
1633 
1634         jClass.addMethod(method);
1635 
1636         // --------------------------------------------------------------------
1637 
1638         method = convertNumericalType(
1639                 "getDoubleValue", JType.DOUBLE, "Double.valueOf( s ).doubleValue()", "a floating point number");
1640 
1641         jClass.addMethod(method);
1642 
1643         // --------------------------------------------------------------------
1644 
1645         method = new JMethod("getDateValue", new JClass("java.util.Date"), null);
1646         method.addException(new JClass("XMLStreamException"));
1647         method.getModifiers().makePrivate();
1648 
1649         method.addParameter(new JParameter(new JClass("String"), "s"));
1650         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1651         method.addParameter(new JParameter(new JClass("String"), "dateFormat"));
1652         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1653         method.addException(new JClass("XMLStreamException"));
1654 
1655         writeDateParsingHelper(
1656                 method.getSourceCode(), "new XMLStreamException( e.getMessage(), xmlStreamReader.getLocation(), e )");
1657 
1658         jClass.addMethod(method);
1659 
1660         // --------------------------------------------------------------------
1661 
1662         method = new JMethod("checkFieldWithDuplicate", JType.BOOLEAN, null);
1663         method.getModifiers().makePrivate();
1664 
1665         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1666         method.addParameter(new JParameter(new JClass("String"), "tagName"));
1667         method.addParameter(new JParameter(new JClass("String"), "alias"));
1668         method.addParameter(new JParameter(new JClass("java.util.Set"), "parsed"));
1669         method.addException(new JClass("XMLStreamException"));
1670 
1671         sc = method.getSourceCode();
1672 
1673         sc.add("if ( !( xmlStreamReader.getLocalName().equals( tagName ) ||"
1674                 + " xmlStreamReader.getLocalName().equals( alias ) ) )");
1675 
1676         sc.add("{");
1677         sc.addIndented("return false;");
1678         sc.add("}");
1679 
1680         sc.add("if ( !parsed.add( tagName ) )");
1681 
1682         sc.add("{");
1683         sc.addIndented(
1684                 "throw new XMLStreamException( \"Duplicated tag: '\" + tagName + \"'\", xmlStreamReader.getLocation() );");
1685         sc.add("}");
1686 
1687         sc.add("return true;");
1688 
1689         jClass.addMethod(method);
1690 
1691         // --------------------------------------------------------------------
1692 
1693         method = new JMethod("checkUnknownElement", null, null);
1694         method.getModifiers().makePrivate();
1695 
1696         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1697         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
1698         method.addException(new JClass("XMLStreamException"));
1699 
1700         sc = method.getSourceCode();
1701 
1702         sc.add("if ( strict )");
1703 
1704         sc.add("{");
1705         sc.addIndented("throw new XMLStreamException( \"Unrecognised tag: '\" + xmlStreamReader.getLocalName() + "
1706                 + "\"'\", xmlStreamReader.getLocation() );");
1707         sc.add("}");
1708 
1709         sc.add("int unrecognizedTagCount = 1;");
1710         sc.add("while( unrecognizedTagCount != 0 )");
1711 
1712         sc.add("{");
1713         sc.indent();
1714 
1715         sc.add("xmlStreamReader.next();");
1716         sc.add("if ( xmlStreamReader.getEventType() == XMLStreamConstants.START_ELEMENT )");
1717 
1718         sc.add("{");
1719         sc.addIndented("unrecognizedTagCount++;");
1720         sc.add("}");
1721 
1722         sc.add("else if ( xmlStreamReader.getEventType() == XMLStreamConstants.END_ELEMENT )");
1723         sc.add("{");
1724         sc.addIndented("unrecognizedTagCount--;");
1725         sc.add("}");
1726 
1727         sc.unindent();
1728         sc.add("}");
1729 
1730         jClass.addMethod(method);
1731 
1732         // --------------------------------------------------------------------
1733 
1734         method = new JMethod("nextTag", JType.INT, null);
1735         method.getModifiers().makePrivate();
1736 
1737         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1738         method.addException(new JClass("XMLStreamException"));
1739 
1740         sc = method.getSourceCode();
1741 
1742         sc.add("while ( true )");
1743         sc.add("{");
1744         sc.indent();
1745         sc.add("int eventType = xmlStreamReader.next();");
1746         sc.add("switch ( eventType )");
1747         sc.add("{");
1748         sc.indent();
1749         sc.add("case XMLStreamConstants.CHARACTERS:");
1750         sc.add("case XMLStreamConstants.CDATA:");
1751         sc.add("case XMLStreamConstants.SPACE:");
1752         sc.add("case XMLStreamConstants.PROCESSING_INSTRUCTION:");
1753         sc.add("case XMLStreamConstants.COMMENT:");
1754         sc.addIndented("break;");
1755         sc.add("case XMLStreamConstants.START_ELEMENT:");
1756         sc.add("case XMLStreamConstants.END_ELEMENT:");
1757         sc.addIndented("return eventType;");
1758         sc.add("default:");
1759         sc.addIndented("throw new XMLStreamException( \"expected start or end tag\", xmlStreamReader.getLocation() );");
1760         sc.unindent();
1761         sc.add("}");
1762         sc.unindent();
1763         sc.add("}");
1764 
1765         jClass.addMethod(method);
1766     }
1767 
1768     private JMethod convertNumericalType(String methodName, JType returnType, String expression, String typeDesc) {
1769         JMethod method = new JMethod(methodName, returnType, null);
1770         method.addException(new JClass("XMLStreamException"));
1771         method.getModifiers().makePrivate();
1772 
1773         method.addParameter(new JParameter(new JClass("String"), "s"));
1774         method.addParameter(new JParameter(new JClass("String"), "attribute"));
1775         method.addParameter(new JParameter(new JClass("XMLStreamReader"), "xmlStreamReader"));
1776         method.addParameter(new JParameter(JType.BOOLEAN, "strict"));
1777 
1778         JSourceCode sc = method.getSourceCode();
1779 
1780         sc.add("if ( s != null )");
1781 
1782         sc.add("{");
1783         sc.indent();
1784 
1785         sc.add("try");
1786 
1787         sc.add("{");
1788         sc.addIndented("return " + expression + ";");
1789         sc.add("}");
1790 
1791         sc.add("catch ( NumberFormatException nfe )");
1792 
1793         sc.add("{");
1794         sc.indent();
1795 
1796         sc.add("if ( strict )");
1797 
1798         sc.add("{");
1799         sc.addIndented("throw new XMLStreamException( \"Unable to parse element '\" + attribute + \"', must be "
1800                 + typeDesc + " but was '\" + s + \"'\", xmlStreamReader.getLocation(), nfe );");
1801         sc.add("}");
1802 
1803         sc.unindent();
1804         sc.add("}");
1805 
1806         sc.unindent();
1807         sc.add("}");
1808 
1809         sc.add("return 0;");
1810 
1811         return method;
1812     }
1813 }