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