001    /*
002     * Copyright (C) 2009 The CC-XJC Project. All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions
006     * are met:
007     *
008     *   o Redistributions of source code must retain the above copyright
009     *     notice, this  list of conditions and the following disclaimer.
010     *
011     *   o Redistributions in binary form must reproduce the above copyright
012     *     notice, this list of conditions and the following disclaimer in
013     *     the documentation and/or other materials provided with the
014     *     distribution.
015     *
016     * THIS SOFTWARE IS PROVIDED BY THE CC-XJC PROJECT AND CONTRIBUTORS "AS IS"
017     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
018     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
019     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CC-XJC PROJECT OR
020     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
021     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
022     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
023     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
024     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
025     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
026     * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027     *
028     * $Id: PluginImpl.java 129 2011-03-11 02:51:49Z schulte2005 $
029     */
030    package net.sourceforge.ccxjc;
031    
032    import com.sun.codemodel.JBlock;
033    import com.sun.codemodel.JCatchBlock;
034    import com.sun.codemodel.JClass;
035    import com.sun.codemodel.JConditional;
036    import com.sun.codemodel.JDefinedClass;
037    import com.sun.codemodel.JExpr;
038    import com.sun.codemodel.JExpression;
039    import com.sun.codemodel.JFieldVar;
040    import com.sun.codemodel.JForLoop;
041    import com.sun.codemodel.JInvocation;
042    import com.sun.codemodel.JMethod;
043    import com.sun.codemodel.JMod;
044    import com.sun.codemodel.JOp;
045    import com.sun.codemodel.JTryBlock;
046    import com.sun.codemodel.JType;
047    import com.sun.codemodel.JVar;
048    import com.sun.tools.xjc.BadCommandLineException;
049    import com.sun.tools.xjc.Options;
050    import com.sun.tools.xjc.Plugin;
051    import com.sun.tools.xjc.generator.bean.ImplStructureStrategy;
052    import com.sun.tools.xjc.model.CAdapter;
053    import com.sun.tools.xjc.model.CArrayInfo;
054    import com.sun.tools.xjc.model.CBuiltinLeafInfo;
055    import com.sun.tools.xjc.model.CClassInfo;
056    import com.sun.tools.xjc.model.CCustomizations;
057    import com.sun.tools.xjc.model.CElementInfo;
058    import com.sun.tools.xjc.model.CEnumLeafInfo;
059    import com.sun.tools.xjc.model.CNonElement;
060    import com.sun.tools.xjc.model.CTypeInfo;
061    import com.sun.tools.xjc.model.CWildcardTypeInfo;
062    import com.sun.tools.xjc.model.nav.NType;
063    import com.sun.tools.xjc.outline.Aspect;
064    import com.sun.tools.xjc.outline.ClassOutline;
065    import com.sun.tools.xjc.outline.FieldOutline;
066    import com.sun.tools.xjc.outline.Outline;
067    import com.sun.xml.bind.v2.model.annotation.Locatable;
068    import com.sun.xml.bind.v2.model.core.ID;
069    import com.sun.xml.bind.v2.runtime.Location;
070    import com.sun.xml.xsom.XSComponent;
071    import com.sun.xml.xsom.XmlString;
072    import java.io.BufferedReader;
073    import java.io.ByteArrayInputStream;
074    import java.io.ByteArrayOutputStream;
075    import java.io.File;
076    import java.io.FileReader;
077    import java.io.IOException;
078    import java.io.InvalidClassException;
079    import java.io.NotSerializableException;
080    import java.io.ObjectInputStream;
081    import java.io.ObjectOutputStream;
082    import java.io.OptionalDataException;
083    import java.io.Serializable;
084    import java.io.StreamCorruptedException;
085    import java.lang.reflect.Array;
086    import java.lang.reflect.InvocationTargetException;
087    import java.math.BigDecimal;
088    import java.math.BigInteger;
089    import java.net.URI;
090    import java.net.URL;
091    import java.text.MessageFormat;
092    import java.util.ArrayList;
093    import java.util.Arrays;
094    import java.util.Calendar;
095    import java.util.Collection;
096    import java.util.Collections;
097    import java.util.Comparator;
098    import java.util.Currency;
099    import java.util.Date;
100    import java.util.HashSet;
101    import java.util.Iterator;
102    import java.util.LinkedList;
103    import java.util.List;
104    import java.util.Locale;
105    import java.util.ResourceBundle;
106    import java.util.Set;
107    import java.util.TimeZone;
108    import java.util.UUID;
109    import java.util.logging.Level;
110    import javax.activation.MimeType;
111    import javax.xml.bind.JAXBElement;
112    import javax.xml.datatype.Duration;
113    import javax.xml.datatype.XMLGregorianCalendar;
114    import javax.xml.namespace.QName;
115    import org.w3c.dom.Element;
116    import org.xml.sax.ErrorHandler;
117    import org.xml.sax.Locator;
118    
119    /**
120     * CC-XJC plugin implementation.
121     *
122     * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
123     * @version $Id: PluginImpl.java 129 2011-03-11 02:51:49Z schulte2005 $
124     */
125    public final class PluginImpl extends Plugin
126    {
127    
128        private static final JType[] NO_ARGS = new JType[ 0 ];
129    
130        private static final String MESSAGE_PREFIX = "CC-XJC";
131    
132        private static final String WARNING_PREFIX = MESSAGE_PREFIX + " WARNING";
133    
134        private static final String OPTION_NAME = "copy-constructor";
135    
136        private static final String VISIBILITY_OPTION_NAME = "-cc-visibility";
137    
138        private static final String TARGET_OPTION_NAME = "-cc-target";
139    
140        private static final String NULLABLE_OPTION_NAME = "-cc-nullable";
141    
142        private static final String HIERARCHICAL_OPTION_NAME = "-cc-hierarchical";
143    
144        private static final String IMMUTABLE_TYPES_OPTION_NAME = "-cc-immutable-types";
145    
146        private static final String CLONEABLE_TYPES_OPTION_NAME = "-cc-cloneable-types";
147    
148        private static final String STRING_TYPES_OPTION_NAME = "-cc-string-types";
149    
150        private static final String ELEMENT_SEPARATOR = ":";
151    
152        private static final List<String> DEFAULT_IMMUTABLE_TYPES = Arrays.asList( new String[]
153            {
154                Boolean.class.getName(),
155                Byte.class.getName(),
156                Character.class.getName(),
157                Double.class.getName(),
158                Enum.class.getName(),
159                Float.class.getName(),
160                Integer.class.getName(),
161                Long.class.getName(),
162                Short.class.getName(),
163                String.class.getName(),
164                BigDecimal.class.getName(),
165                BigInteger.class.getName(),
166                UUID.class.getName(),
167                QName.class.getName(),
168                Duration.class.getName(),
169                Currency.class.getName()
170            } );
171    
172        private static final List<String> DEFAULT_CLONEABLE_TYPES = Arrays.asList( new String[]
173            {
174                XMLGregorianCalendar.class.getName(),
175                Date.class.getName(),
176                Calendar.class.getName(),
177                TimeZone.class.getName(),
178                Locale.class.getName()
179            } );
180    
181        private static final List<String> DEFAULT_STRING_TYPES = Arrays.asList( new String[]
182            {
183                File.class.getName(),
184                URI.class.getName(),
185                URL.class.getName(),
186                MimeType.class.getName()
187            } );
188    
189        private static final Class<?>[] PRIMITIVE_ARRAY_TYPES =
190        {
191            boolean[].class,
192            byte[].class,
193            char[].class,
194            double[].class,
195            float[].class,
196            int[].class,
197            long[].class,
198            short[].class
199        };
200    
201        private static final String[] VISIBILITY_ARGUMENTS =
202        {
203            "private", "package", "protected", "public"
204        };
205    
206        private static final String[] TARGET_ARGUMENTS =
207        {
208            "1.5", "1.6", "1.7"
209        };
210    
211        private static final int TARGET_1_5 = 5;
212    
213        private static final int TARGET_1_6 = 6;
214    
215        private static final int TARGET_1_7 = 7;
216    
217        private boolean success;
218    
219        private Options options;
220    
221        private String visibility = "private";
222    
223        private int targetJdk = TARGET_1_5;
224    
225        private boolean nullable = false;
226    
227        private boolean hierarchical = false;
228    
229        private final List<String> immutableTypes = new ArrayList<String>( 64 );
230    
231        private final List<String> cloneableTypes = new ArrayList<String>( 64 );
232    
233        private final List<String> stringTypes = new ArrayList<String>( 64 );
234    
235        private BigInteger methodCount;
236    
237        private BigInteger constructorCount;
238    
239        private BigInteger expressionCount;
240    
241        private final Set<Class<?>> contextExceptions = new HashSet<Class<?>>();
242    
243        @Override
244        public String getOptionName()
245        {
246            return OPTION_NAME;
247        }
248    
249        @Override
250        public String getUsage()
251        {
252            final String n = System.getProperty( "line.separator", "\n" );
253    
254            return new StringBuilder( 1024 ).append( "  -" ).append( OPTION_NAME ).append( "  :  " ).
255                append( getMessage( "usage" ) ).append( n ).
256                append( "  " ).append( VISIBILITY_OPTION_NAME ).append( "     :  " ).
257                append( getMessage( "visibilityUsage" ) ).append( n ).
258                append( "  " ).append( TARGET_OPTION_NAME ).append( "         :  " ).
259                append( getMessage( "targetUsage" ) ).append( n ).
260                append( "  " ).append( NULLABLE_OPTION_NAME ).append( "       :  " ).
261                append( getMessage( "nullableUsage" ) ).append( n ).
262                append( "  " ).append( HIERARCHICAL_OPTION_NAME ).append( "   :  " ).
263                append( getMessage( "hierarchicalUsage" ) ).append( n ).
264                append( "  " ).append( CLONEABLE_TYPES_OPTION_NAME ).append( ":  " ).
265                append( getMessage( "cloneableTypesUsage", ELEMENT_SEPARATOR ) ).append( n ).
266                append( "  " ).append( IMMUTABLE_TYPES_OPTION_NAME ).append( ":  " ).
267                append( getMessage( "immutableTypesUsage", ELEMENT_SEPARATOR ) ).append( n ).
268                append( "  " ).append( STRING_TYPES_OPTION_NAME ).append( "   :  " ).
269                append( getMessage( "stringTypesUsage", ELEMENT_SEPARATOR ) ).toString();
270    
271        }
272    
273        @Override
274        public int parseArgument( final Options opt, final String[] args, final int i )
275            throws BadCommandLineException, IOException
276        {
277            final StringBuilder supportedVisibilities = new StringBuilder( 1024 ).append( '[' );
278            for ( Iterator<String> it = Arrays.asList( VISIBILITY_ARGUMENTS ).iterator(); it.hasNext(); )
279            {
280                supportedVisibilities.append( it.next() );
281                if ( it.hasNext() )
282                {
283                    supportedVisibilities.append( ", " );
284                }
285            }
286    
287            final StringBuilder supportedTargets = new StringBuilder( 512 ).append( '[' );
288            for ( Iterator<String> it = Arrays.asList( TARGET_ARGUMENTS ).iterator(); it.hasNext(); )
289            {
290                supportedTargets.append( it.next() );
291                if ( it.hasNext() )
292                {
293                    supportedTargets.append( ", " );
294                }
295            }
296    
297            if ( args[i].startsWith( VISIBILITY_OPTION_NAME ) )
298            {
299                if ( i + 1 >= args.length )
300                {
301                    final String missingOptionArgument = getMessage( "missingOptionArgument", VISIBILITY_OPTION_NAME );
302                    final String expectedOptionArgument = getMessage( "expectedOptionArgument",
303                                                                      supportedVisibilities.append( ']' ).toString() );
304    
305                    throw new BadCommandLineException( missingOptionArgument + " " + expectedOptionArgument );
306                }
307    
308                this.visibility = args[i + 1].trim();
309    
310                boolean supported = false;
311                for ( String argument : VISIBILITY_ARGUMENTS )
312                {
313                    if ( argument.equals( this.visibility ) )
314                    {
315                        supported = true;
316                        break;
317                    }
318                }
319    
320                if ( !supported )
321                {
322                    final String expectedOptionArgument = getMessage( "expectedOptionArgument",
323                                                                      supportedVisibilities.append( ']' ).toString() );
324    
325                    throw new BadCommandLineException( expectedOptionArgument );
326                }
327    
328                return 2;
329            }
330    
331            if ( args[i].startsWith( TARGET_OPTION_NAME ) )
332            {
333                if ( i + 1 >= args.length )
334                {
335                    final String missingOptionArgument = getMessage( "missingOptionArgument", TARGET_OPTION_NAME );
336                    final String expectedOptionArgument = getMessage( "expectedOptionArgument",
337                                                                      supportedTargets.append( ']' ).toString() );
338    
339                    throw new BadCommandLineException( missingOptionArgument + " " + expectedOptionArgument );
340                }
341    
342                final String targetArg = args[i + 1].trim();
343    
344                boolean supported = false;
345                for ( String argument : TARGET_ARGUMENTS )
346                {
347                    if ( argument.equals( targetArg ) )
348                    {
349                        supported = true;
350                        break;
351                    }
352                }
353    
354                if ( !supported )
355                {
356                    final String expectedOptionArgument = getMessage( "expectedOptionArgument",
357                                                                      supportedTargets.append( ']' ).toString() );
358    
359                    throw new BadCommandLineException( expectedOptionArgument );
360                }
361    
362                if ( targetArg.equals( "1.5" ) )
363                {
364                    this.targetJdk = TARGET_1_5;
365                }
366                else if ( targetArg.equals( "1.6" ) )
367                {
368                    this.targetJdk = TARGET_1_6;
369                }
370                else if ( targetArg.equals( "1.7" ) )
371                {
372                    this.targetJdk = TARGET_1_7;
373                }
374    
375                return 2;
376            }
377    
378            if ( args[i].startsWith( NULLABLE_OPTION_NAME ) )
379            {
380                this.nullable = true;
381                return 1;
382            }
383    
384            if ( args[i].startsWith( HIERARCHICAL_OPTION_NAME ) )
385            {
386                this.hierarchical = true;
387                return 1;
388            }
389    
390            if ( args[i].startsWith( IMMUTABLE_TYPES_OPTION_NAME ) )
391            {
392                if ( i + 1 >= args.length )
393                {
394                    throw new BadCommandLineException( getMessage( "missingOptionArgument", IMMUTABLE_TYPES_OPTION_NAME ) );
395                }
396    
397                final Collection<String> types = Arrays.asList( args[i + 1].split( ELEMENT_SEPARATOR ) );
398                for ( String type : types )
399                {
400                    if ( type.startsWith( "@" ) )
401                    {
402                        this.immutableTypes.addAll( this.readTypes( type.substring( 1 ) ) );
403                    }
404                    else if ( type.trim().length() > 0 )
405                    {
406                        this.immutableTypes.add( type );
407                    }
408                }
409    
410                return 2;
411            }
412    
413            if ( args[i].startsWith( CLONEABLE_TYPES_OPTION_NAME ) )
414            {
415                if ( i + 1 >= args.length )
416                {
417                    throw new BadCommandLineException( getMessage( "missingOptionArgument", CLONEABLE_TYPES_OPTION_NAME ) );
418                }
419    
420                final Collection<String> types = Arrays.asList( args[i + 1].split( ELEMENT_SEPARATOR ) );
421    
422                for ( String type : types )
423                {
424                    if ( type.startsWith( "@" ) )
425                    {
426                        this.cloneableTypes.addAll( this.readTypes( type.substring( 1 ) ) );
427                    }
428                    else if ( type.trim().length() > 0 )
429                    {
430                        this.cloneableTypes.add( type );
431                    }
432                }
433    
434                return 2;
435            }
436    
437            if ( args[i].startsWith( STRING_TYPES_OPTION_NAME ) )
438            {
439                if ( i + 1 >= args.length )
440                {
441                    throw new BadCommandLineException( getMessage( "missingOptionArgument", STRING_TYPES_OPTION_NAME ) );
442                }
443    
444                final Collection<String> types = Arrays.asList( args[i + 1].split( ELEMENT_SEPARATOR ) );
445    
446                for ( String type : types )
447                {
448                    if ( type.startsWith( "@" ) )
449                    {
450                        this.stringTypes.addAll( this.readTypes( type.substring( 1 ) ) );
451                    }
452                    else if ( type.trim().length() > 0 )
453                    {
454                        this.stringTypes.add( type );
455                    }
456                }
457    
458                return 2;
459            }
460    
461            return 0;
462        }
463    
464        @Override
465        public boolean run( final Outline model, final Options options, final ErrorHandler errorHandler )
466        {
467            this.success = true;
468            this.options = options;
469            this.methodCount = BigInteger.ZERO;
470            this.constructorCount = BigInteger.ZERO;
471            this.expressionCount = BigInteger.ZERO;
472    
473            this.cloneableTypes.removeAll( DEFAULT_CLONEABLE_TYPES );
474            this.cloneableTypes.addAll( DEFAULT_CLONEABLE_TYPES );
475            this.immutableTypes.removeAll( DEFAULT_IMMUTABLE_TYPES );
476            this.immutableTypes.addAll( DEFAULT_IMMUTABLE_TYPES );
477            this.stringTypes.removeAll( DEFAULT_STRING_TYPES );
478            this.stringTypes.addAll( DEFAULT_STRING_TYPES );
479    
480            this.log( Level.INFO, "title" );
481            this.log( Level.INFO, "visibilityReport", this.visibility );
482    
483            final StringBuilder cloneableInfo = new StringBuilder( 1024 );
484            final StringBuilder immutableInfo = new StringBuilder( 1024 );
485            final StringBuilder stringInfo = new StringBuilder( 1024 );
486    
487            for ( String name : this.cloneableTypes )
488            {
489                cloneableInfo.append( System.getProperty( "line.separator", "\n" ) ).append( "\t" ).append( name );
490            }
491    
492            for ( String name : this.immutableTypes )
493            {
494                immutableInfo.append( System.getProperty( "line.separator", "\n" ) ).append( "\t" ).append( name );
495            }
496    
497            for ( String name : this.stringTypes )
498            {
499                stringInfo.append( System.getProperty( "line.separator", "\n" ) ).append( "\t" ).append( name );
500            }
501    
502            this.log( Level.INFO, "cloneableTypesInfo", cloneableInfo.toString() );
503            this.log( Level.INFO, "immutableTypesInfo", immutableInfo.toString() );
504            this.log( Level.INFO, "stringTypesInfo", stringInfo.toString() );
505    
506            for ( ClassOutline clazz : model.getClasses() )
507            {
508                this.warnOnReferencedSupertypes( clazz );
509    
510                if ( this.getStandardConstructor( clazz ) == null )
511                {
512                    this.log( Level.WARNING, "couldNotAddStdCtor", clazz.implClass.binaryName() );
513                }
514    
515                if ( this.getCopyConstructor( clazz ) == null )
516                {
517                    this.log( Level.WARNING, "couldNotAddCopyCtor", clazz.implClass.binaryName() );
518                }
519    
520                if ( this.getCloneMethod( clazz ) == null )
521                {
522                    this.log( Level.WARNING, "couldNotAddMethod", "clone", clazz.implClass.binaryName() );
523                }
524            }
525    
526            this.log( Level.INFO, "report", this.methodCount, this.constructorCount, this.expressionCount );
527    
528            this.options = null;
529            return this.success;
530        }
531    
532        private int getVisibilityModifier()
533        {
534            if ( "private".equals( this.visibility ) )
535            {
536                return JMod.PRIVATE;
537            }
538            else if ( "protected".equals( this.visibility ) )
539            {
540                return JMod.PROTECTED;
541            }
542            else if ( "public".equals( this.visibility ) )
543            {
544                return JMod.PUBLIC;
545            }
546    
547            return JMod.NONE;
548        }
549    
550        private boolean isTargetSupported( final int target )
551        {
552            return target <= this.targetJdk;
553        }
554    
555        private JMethod getStandardConstructor( final ClassOutline clazz )
556        {
557            JMethod ctor = clazz.implClass.getConstructor( NO_ARGS );
558            if ( ctor == null )
559            {
560                ctor = this.generateStandardConstructor( clazz );
561            }
562            else
563            {
564                this.log( Level.WARNING, "standardCtorExists", clazz.implClass.binaryName() );
565            }
566    
567            return ctor;
568        }
569    
570        private JMethod getCopyConstructor( final ClassOutline clazz )
571        {
572            JMethod ctor = clazz.implClass.getConstructor( new JType[]
573                {
574                    clazz.implClass
575                } );
576    
577            if ( ctor == null )
578            {
579                ctor = this.generateCopyConstructor( clazz );
580            }
581            else
582            {
583                this.log( Level.WARNING, "copyCtorExists", clazz.implClass.binaryName() );
584            }
585    
586            return ctor;
587        }
588    
589        private JMethod getCloneMethod( final ClassOutline clazz )
590        {
591            JMethod clone = clazz.implClass.getMethod( "clone", NO_ARGS );
592            if ( clone == null )
593            {
594                clone = this.generateCloneMethod( clazz );
595            }
596            else
597            {
598                this.log( Level.WARNING, "methodExists", "clone", clazz.implClass.binaryName() );
599            }
600    
601            return clone;
602        }
603    
604        private JMethod getPropertyGetter( final FieldOutline f )
605        {
606            final JDefinedClass clazz = f.parent().implClass;
607            final String name = f.getPropertyInfo().getName( true );
608            JMethod getter = clazz.getMethod( "get" + name, NO_ARGS );
609    
610            if ( getter == null )
611            {
612                getter = clazz.getMethod( "is" + name, NO_ARGS );
613            }
614    
615            return getter;
616        }
617    
618        private FieldOutline getFieldOutline( final ClassOutline clazz, final String fieldName )
619        {
620            for ( FieldOutline f : clazz.getDeclaredFields() )
621            {
622                if ( f.getPropertyInfo().getName( false ).equals( fieldName ) )
623                {
624                    return f;
625                }
626            }
627    
628            return null;
629        }
630    
631        private JInvocation getCopyOfJaxbElementInvocation( final ClassOutline clazz )
632        {
633            final JClass jaxbElement = clazz.parent().getCodeModel().ref( JAXBElement.class );
634            final JType[] signature =
635            {
636                jaxbElement
637            };
638    
639            final String methodName = "copyOFJAXBElement";
640            final int mod = this.getVisibilityModifier();
641    
642            if ( mod != JMod.PRIVATE )
643            {
644                for ( JMethod m : clazz._package().objectFactory().methods() )
645                {
646                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
647                    {
648                        return clazz._package().objectFactory().staticInvoke( m );
649                    }
650                }
651            }
652            else
653            {
654                for ( JMethod m : clazz.implClass.methods() )
655                {
656                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
657                    {
658                        return JExpr.invoke( m );
659                    }
660                }
661            }
662    
663            final JMethod m =
664                ( mod != JMod.PRIVATE
665                  ? clazz._package().objectFactory().method( JMod.STATIC | mod, JAXBElement.class, methodName )
666                  : clazz.implClass.method( JMod.STATIC | mod, JAXBElement.class, methodName ) );
667    
668            final JVar element = m.param( JMod.FINAL, jaxbElement, "element" );
669    
670            m.javadoc().append( "Creates and returns a deep copy of a given {@code JAXBElement} instance." );
671            m.javadoc().addParam( element ).append( "The instance to copy or {@code null}." );
672            m.javadoc().addReturn().append(
673                "A deep copy of {@code element} or {@code null} if {@code element} is {@code null}." );
674    
675            m.body().directStatement( "// " + getMessage( "title" ) );
676    
677            final JConditional isNotNull = m.body()._if( element.ne( JExpr._null() ) );
678            final JExpression newElement = JExpr._new( jaxbElement ).
679                arg( JExpr.invoke( element, "getName" ) ).
680                arg( JExpr.invoke( element, "getDeclaredType" ) ).
681                arg( JExpr.invoke( element, "getScope" ) ).
682                arg( JExpr.invoke( element, "getValue" ) );
683    
684            final JVar copy = isNotNull._then().decl( JMod.FINAL, jaxbElement, "copy", newElement );
685            isNotNull._then().add( copy.invoke( "setNil" ).arg( element.invoke( "isNil" ) ) );
686            isNotNull._then().add( copy.invoke( "setValue" ).arg( this.getCopyOfObjectInvocation( clazz ).
687                arg( JExpr.invoke( copy, "getValue" ) ) ) );
688    
689            isNotNull._then()._return( copy );
690            m.body()._return( JExpr._null() );
691            this.methodCount = this.methodCount.add( BigInteger.ONE );
692            return ( mod != JMod.PRIVATE ? clazz._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
693        }
694    
695        private JExpression getCopyOfPrimitiveArrayExpression( final ClassOutline classOutline, final JClass arrayType,
696                                                               final JExpression source )
697        {
698            if ( this.isTargetSupported( TARGET_1_6 ) )
699            {
700                final JClass arrays = classOutline.parent().getCodeModel().ref( Arrays.class );
701                return JOp.cond( source.eq( JExpr._null() ), JExpr._null(), arrays.staticInvoke( "copyOf" ).
702                    arg( source ).arg( source.ref( "length" ) ) );
703    
704            }
705    
706            final JClass array = classOutline.parent().getCodeModel().ref( Array.class );
707            final JClass system = classOutline.parent().getCodeModel().ref( System.class );
708    
709            final int mod = this.getVisibilityModifier();
710            final String methodName = "copyOf";
711    
712            if ( mod != JMod.PRIVATE )
713            {
714                for ( JMethod m : classOutline._package().objectFactory().methods() )
715                {
716                    if ( m.name().equals( methodName ) )
717                    {
718                        final JType[] signature = m.listParamTypes();
719                        if ( signature.length == 1 && signature[0].binaryName().equals( arrayType.binaryName() ) )
720                        {
721                            return classOutline._package().objectFactory().staticInvoke( m ).arg( source );
722                        }
723                    }
724                }
725            }
726            else
727            {
728                for ( JMethod m : classOutline.implClass.methods() )
729                {
730                    if ( m.name().equals( methodName ) )
731                    {
732                        final JType[] signature = m.listParamTypes();
733                        if ( signature.length == 1 && signature[0].binaryName().equals( arrayType.binaryName() ) )
734                        {
735                            return JExpr.invoke( m ).arg( source );
736                        }
737                    }
738                }
739            }
740    
741            final JMethod m =
742                ( mod != JMod.PRIVATE
743                  ? classOutline._package().objectFactory().method( JMod.STATIC | mod, arrayType, methodName )
744                  : classOutline.implClass.method( JMod.STATIC | mod, arrayType, methodName ) );
745    
746            final JVar arrayParam = m.param( JMod.FINAL, arrayType, "array" );
747    
748            m.javadoc().append( "Creates and returns a deep copy of a given array." );
749            m.javadoc().addParam( arrayParam ).append( "The array to copy or {@code null}." );
750            m.javadoc().addReturn().append(
751                "A deep copy of {@code array} or {@code null} if {@code array} is {@code null}." );
752    
753            m.body().directStatement( "// " + getMessage( "title" ) );
754    
755            final JConditional arrayNotNull = m.body()._if( arrayParam.ne( JExpr._null() ) );
756            final JVar copy = arrayNotNull._then().decl(
757                JMod.FINAL, arrayType, "copy", JExpr.cast( arrayType, array.staticInvoke( "newInstance" ).arg(
758                arrayParam.invoke( "getClass" ).invoke( "getComponentType" ) ).arg( arrayParam.ref( "length" ) ) ) );
759    
760            arrayNotNull._then().add( system.staticInvoke( "arraycopy" ).arg( arrayParam ).arg( JExpr.lit( 0 ) ).
761                arg( copy ).arg( JExpr.lit( 0 ) ).arg( arrayParam.ref( "length" ) ) );
762    
763            arrayNotNull._then()._return( copy );
764    
765            m.body()._return( JExpr._null() );
766    
767            this.methodCount = this.methodCount.add( BigInteger.ONE );
768            return ( mod != JMod.PRIVATE ? classOutline._package().objectFactory().staticInvoke( m ).arg( source )
769                     : JExpr.invoke( m ).arg( source ) );
770    
771        }
772    
773        private JInvocation getCopyOfArrayInvocation( final ClassOutline clazz )
774        {
775            final JClass object = clazz.parent().getCodeModel().ref( Object.class );
776            final JClass array = clazz.parent().getCodeModel().ref( Array.class );
777    
778            final JType[] signature =
779            {
780                object
781            };
782    
783            final String methodName = "copyOfArray";
784            final int mod = this.getVisibilityModifier();
785    
786            if ( mod != JMod.PRIVATE )
787            {
788                for ( JMethod m : clazz._package().objectFactory().methods() )
789                {
790                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
791                    {
792                        return clazz._package().objectFactory().staticInvoke( m );
793                    }
794                }
795            }
796            else
797            {
798                for ( JMethod m : clazz.implClass.methods() )
799                {
800                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
801                    {
802                        return JExpr.invoke( m );
803                    }
804                }
805            }
806    
807            final JMethod m =
808                ( mod != JMod.PRIVATE
809                  ? clazz._package().objectFactory().method( JMod.STATIC | mod, object, methodName )
810                  : clazz.implClass.method( JMod.STATIC | mod, object, methodName ) );
811    
812            final JVar arrayArg = m.param( JMod.FINAL, object, "array" );
813    
814            m.javadoc().append( "Creates and returns a deep copy of a given array." );
815            m.javadoc().addParam( arrayArg ).append( "The array to copy or {@code null}." );
816            m.javadoc().addReturn().append(
817                "A deep copy of {@code array} or {@code null} if {@code array} is {@code null}." );
818    
819            m.body().directStatement( "// " + getMessage( "title" ) );
820    
821            final JConditional arrayNotNull = m.body()._if( arrayArg.ne( JExpr._null() ) );
822    
823            for ( Class<?> a : PRIMITIVE_ARRAY_TYPES )
824            {
825                final JClass primitiveArray = clazz.parent().getCodeModel().ref( a );
826                final JConditional isArrayOfPrimitive =
827                    arrayNotNull._then()._if( arrayArg.invoke( "getClass" ).eq( primitiveArray.dotclass() ) );
828    
829                isArrayOfPrimitive._then()._return( this.getCopyOfPrimitiveArrayExpression(
830                    clazz, primitiveArray, JExpr.cast( primitiveArray, arrayArg ) ) );
831    
832            }
833    
834            final JVar len = arrayNotNull._then().decl(
835                JMod.FINAL, clazz.parent().getCodeModel().INT, "len", array.staticInvoke( "getLength" ).
836                arg( arrayArg ) );
837    
838            final JVar copy = arrayNotNull._then().decl( JMod.FINAL, object, "copy", array.staticInvoke( "newInstance" ).
839                arg( arrayArg.invoke( "getClass" ).invoke( "getComponentType" ) ).arg( len ) );
840    
841            final JForLoop forEachRef = arrayNotNull._then()._for();
842            final JVar i = forEachRef.init( clazz.parent().getCodeModel().INT, "i", len.minus( JExpr.lit( 1 ) ) );
843            forEachRef.test( i.gte( JExpr.lit( 0 ) ) );
844            forEachRef.update( i.decr() );
845            forEachRef.body().add( array.staticInvoke( "set" ).arg( copy ).arg( i ).
846                arg( this.getCopyOfObjectInvocation( clazz ).arg( array.staticInvoke( "get" ).
847                arg( arrayArg ).arg( i ) ) ) );
848    
849            arrayNotNull._then()._return( copy );
850            m.body()._return( JExpr._null() );
851            this.methodCount = this.methodCount.add( BigInteger.ONE );
852            return ( mod != JMod.PRIVATE ? clazz._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
853        }
854    
855        private JInvocation getCopyOfSerializableInvocation( final ClassOutline clazz )
856        {
857            final JClass serializable = clazz.parent().getCodeModel().ref( Serializable.class );
858            final JClass byteArrayOutputStream = clazz.parent().getCodeModel().ref( ByteArrayOutputStream.class );
859            final JClass byteArrayInputStream = clazz.parent().getCodeModel().ref( ByteArrayInputStream.class );
860            final JClass objectOutputStream = clazz.parent().getCodeModel().ref( ObjectOutputStream.class );
861            final JClass objectInputStream = clazz.parent().getCodeModel().ref( ObjectInputStream.class );
862            final JClass ioException = clazz.parent().getCodeModel().ref( IOException.class );
863            final JClass invalidClass = clazz.parent().getCodeModel().ref( InvalidClassException.class );
864            final JClass notSerializable = clazz.parent().getCodeModel().ref( NotSerializableException.class );
865            final JClass streamCorrupted = clazz.parent().getCodeModel().ref( StreamCorruptedException.class );
866            final JClass securityException = clazz.parent().getCodeModel().ref( SecurityException.class );
867            final JClass optionalData = clazz.parent().getCodeModel().ref( OptionalDataException.class );
868            final JClass classNotFound = clazz.parent().getCodeModel().ref( ClassNotFoundException.class );
869            final JClass assertionError = clazz.parent().getCodeModel().ref( AssertionError.class );
870            final JType[] signature =
871            {
872                serializable
873            };
874    
875            final String methodName = "copyOfSerializable";
876            final int mod = this.getVisibilityModifier();
877    
878            if ( mod != JMod.PRIVATE )
879            {
880                for ( JMethod m : clazz._package().objectFactory().methods() )
881                {
882                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
883                    {
884                        return clazz._package().objectFactory().staticInvoke( m );
885                    }
886                }
887            }
888            else
889            {
890                for ( JMethod m : clazz.implClass.methods() )
891                {
892                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
893                    {
894                        return JExpr.invoke( m );
895                    }
896                }
897            }
898    
899            final JMethod m =
900                ( mod != JMod.PRIVATE
901                  ? clazz._package().objectFactory().method( JMod.STATIC | mod, serializable, methodName )
902                  : clazz.implClass.method( JMod.STATIC | mod, serializable, methodName ) );
903    
904            final JVar s = m.param( JMod.FINAL, serializable, "serializable" );
905    
906            m.javadoc().append( "Creates and returns a deep copy of a given {@code Serializable}." );
907            m.javadoc().addParam( s ).append( "The instance to copy or {@code null}." );
908            m.javadoc().addReturn().append(
909                "A deep copy of {@code serializable} or {@code null} if {@code serializable} is {@code null}." );
910    
911            m.body().directStatement( "// " + getMessage( "title" ) );
912    
913            final JConditional sNotNull = m.body()._if( s.ne( JExpr._null() ) );
914            final JTryBlock tryClone = sNotNull._then()._try();
915    
916            final JVar byteArrayOutput = tryClone.body().decl(
917                JMod.FINAL, byteArrayOutputStream, "byteArrayOutput", JExpr._new( byteArrayOutputStream ) );
918    
919            final JVar objectOutput = tryClone.body().decl(
920                JMod.FINAL, objectOutputStream, "out", JExpr._new( objectOutputStream ).arg( byteArrayOutput ) );
921    
922            tryClone.body().add( objectOutput.invoke( "writeObject" ).arg( s ) );
923            tryClone.body().add( objectOutput.invoke( "close" ) );
924    
925            final JVar byteArrayInput = tryClone.body().decl(
926                JMod.FINAL, byteArrayInputStream, "byteArrayInput",
927                JExpr._new( byteArrayInputStream ).arg( byteArrayOutput.invoke( "toByteArray" ) ) );
928    
929            final JVar objectInput = tryClone.body().decl(
930                JMod.FINAL, objectInputStream, "in", JExpr._new( objectInputStream ).arg( byteArrayInput ) );
931    
932            final JVar copy = tryClone.body().decl(
933                JMod.FINAL, serializable, "copy", JExpr.cast( serializable, objectInput.invoke( "readObject" ) ) );
934    
935            tryClone.body().invoke( objectInput, "close" );
936            tryClone.body()._return( copy );
937    
938            final JExpression assertionErrorMsg =
939                JExpr.lit( "Unexpected instance during copying object '" ).plus( s ).plus( JExpr.lit( "'." ) );
940    
941            final JCatchBlock catchSecurityException = tryClone._catch( securityException );
942            catchSecurityException.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
943                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchSecurityException.param( "e" ) ) ) );
944    
945            final JCatchBlock catchClassNotFound = tryClone._catch( classNotFound );
946            catchClassNotFound.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
947                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchClassNotFound.param( "e" ) ) ) );
948    
949            final JCatchBlock catchInvalidClass = tryClone._catch( invalidClass );
950            catchInvalidClass.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
951                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchInvalidClass.param( "e" ) ) ) );
952    
953            final JCatchBlock catchNotSerializable = tryClone._catch( notSerializable );
954            catchNotSerializable.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
955                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchNotSerializable.param( "e" ) ) ) );
956    
957            final JCatchBlock catchStreamCorrupted = tryClone._catch( streamCorrupted );
958            catchStreamCorrupted.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
959                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchStreamCorrupted.param( "e" ) ) ) );
960    
961            final JCatchBlock catchOptionalData = tryClone._catch( optionalData );
962            catchOptionalData.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
963                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchOptionalData.param( "e" ) ) ) );
964    
965            final JCatchBlock catchIOException = tryClone._catch( ioException );
966            catchIOException.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
967                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchIOException.param( "e" ) ) ) );
968    
969            m.body()._return( JExpr._null() );
970            this.methodCount = this.methodCount.add( BigInteger.ONE );
971            return ( mod != JMod.PRIVATE ? clazz._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
972        }
973    
974        private JInvocation getCopyOfObjectInvocation( final ClassOutline clazz )
975        {
976            final JClass object = clazz.parent().getCodeModel().ref( Object.class );
977            final JClass element = clazz.parent().getCodeModel().ref( Element.class );
978            final JClass jaxbElement = clazz.parent().getCodeModel().ref( JAXBElement.class );
979            final JClass noSuchMethod = clazz.parent().getCodeModel().ref( NoSuchMethodException.class );
980            final JClass illegalAccess = clazz.parent().getCodeModel().ref( IllegalAccessException.class );
981            final JClass invocationTarget = clazz.parent().getCodeModel().ref( InvocationTargetException.class );
982            final JClass securityException = clazz.parent().getCodeModel().ref( SecurityException.class );
983            final JClass illegalArgument = clazz.parent().getCodeModel().ref( IllegalArgumentException.class );
984            final JClass initializerError = clazz.parent().getCodeModel().ref( ExceptionInInitializerError.class );
985            final JClass assertionError = clazz.parent().getCodeModel().ref( AssertionError.class );
986            final JClass classArray = clazz.parent().getCodeModel().ref( Class[].class );
987            final JClass objectArray = clazz.parent().getCodeModel().ref( Object[].class );
988            final JClass serializable = clazz.parent().getCodeModel().ref( Serializable.class );
989    
990            final String methodName = "copyOfObject";
991            final int mod = this.getVisibilityModifier();
992    
993            if ( mod != JMod.PRIVATE )
994            {
995                for ( JMethod m : clazz._package().objectFactory().methods() )
996                {
997                    if ( m.name().equals( methodName ) )
998                    {
999                        return clazz._package().objectFactory().staticInvoke( m );
1000                    }
1001                }
1002            }
1003            else
1004            {
1005                for ( JMethod m : clazz.implClass.methods() )
1006                {
1007                    if ( m.name().equals( methodName ) )
1008                    {
1009                        return JExpr.invoke( m );
1010                    }
1011                }
1012            }
1013    
1014            final JMethod m =
1015                ( mod != JMod.PRIVATE
1016                  ? clazz._package().objectFactory().method( JMod.STATIC | mod, object, methodName )
1017                  : clazz.implClass.method( JMod.STATIC | mod, object, methodName ) );
1018    
1019            final JVar o = m.param( JMod.FINAL, object, "o" );
1020            final Set<Class<?>> exceptions = new HashSet<Class<?>>();
1021    
1022            m.javadoc().append( "Creates and returns a deep copy of a given object." );
1023            m.javadoc().addParam( o ).append( "The instance to copy or {@code null}." );
1024            m.javadoc().addReturn().append( "A deep copy of {@code o} or {@code null} if {@code o} is {@code null}." );
1025    
1026            m.body().directStatement( "// " + getMessage( "title" ) );
1027    
1028            final JBlock copyBlock = new JBlock( false, false );
1029            final JConditional objectNotNull = copyBlock._if( o.ne( JExpr._null() ) );
1030    
1031            final JConditional isPrimitive =
1032                objectNotNull._then()._if( JExpr.invoke( JExpr.invoke( o, "getClass" ), "isPrimitive" ) );
1033    
1034            isPrimitive._then()._return( o );
1035    
1036            final JConditional isArray =
1037                objectNotNull._then()._if( JExpr.invoke( JExpr.invoke( o, "getClass" ), "isArray" ) );
1038    
1039            isArray._then()._return( this.getCopyOfArrayInvocation( clazz ).arg( o ) );
1040    
1041            objectNotNull._then().directStatement( "// Immutable types." );
1042    
1043            for ( String immutableType : this.immutableTypes )
1044            {
1045                final JClass immutable = clazz.parent().getCodeModel().ref( immutableType );
1046                objectNotNull._then()._if( o._instanceof( immutable ) )._then()._return( o );
1047            }
1048    
1049            objectNotNull._then().directStatement( "// String based types." );
1050    
1051            for ( String stringType : this.stringTypes )
1052            {
1053                final JClass string = clazz.parent().getCodeModel().ref( stringType );
1054                final Class<?> c = this.getClass( stringType );
1055    
1056                if ( c != null )
1057                {
1058                    try
1059                    {
1060                        exceptions.addAll( Arrays.asList( c.getConstructor( String.class ).getExceptionTypes() ) );
1061                    }
1062                    catch ( final NoSuchMethodException e )
1063                    {
1064                        // Generated code won't compile.
1065                    }
1066                }
1067    
1068                objectNotNull._then()._if( o._instanceof( string ) )._then()._return(
1069                    JExpr._new( string ).arg( o.invoke( "toString" ) ) );
1070    
1071            }
1072    
1073            objectNotNull._then().directStatement( "// Cloneable types." );
1074    
1075            for ( String cloneableType : this.cloneableTypes )
1076            {
1077                final JClass cloneable = clazz.parent().getCodeModel().ref( cloneableType );
1078                final Class<?> c = this.getClass( cloneableType );
1079    
1080                if ( c != null )
1081                {
1082                    try
1083                    {
1084                        exceptions.addAll( Arrays.asList( c.getMethod( "clone" ).getExceptionTypes() ) );
1085                    }
1086                    catch ( final NoSuchMethodException e )
1087                    {
1088                        // Generated code won't compile.
1089                    }
1090                }
1091    
1092                objectNotNull._then()._if( o._instanceof( cloneable ) )._then()._return(
1093                    JExpr.invoke( JExpr.cast( cloneable, o ), ( "clone" ) ) );
1094    
1095            }
1096    
1097            final JConditional instanceOfDOMElement = objectNotNull._then()._if( o._instanceof( element ) );
1098            instanceOfDOMElement._then()._return( JExpr.cast( element, JExpr.invoke(
1099                JExpr.cast( element, o ), "cloneNode" ).arg( JExpr.TRUE ) ) );
1100    
1101            final JConditional instanceOfElement = objectNotNull._then()._if( o._instanceof( jaxbElement ) );
1102            instanceOfElement._then()._return( this.getCopyOfJaxbElementInvocation( clazz ).
1103                arg( JExpr.cast( jaxbElement, o ) ) );
1104    
1105            final JTryBlock tryCloneMethod = objectNotNull._then()._try();
1106            tryCloneMethod.body()._return( JExpr.invoke( JExpr.invoke( JExpr.invoke( o, "getClass" ), "getMethod" ).
1107                arg( "clone" ).arg( JExpr.cast( classArray, JExpr._null() ) ), "invoke" ).arg( o ).
1108                arg( JExpr.cast( objectArray, JExpr._null() ) ) );
1109    
1110            final JExpression assertionErrorMsg =
1111                JExpr.lit( "Unexpected instance during copying object '" ).plus( o ).plus( JExpr.lit( "'." ) );
1112    
1113            final JCatchBlock catchNoSuchMethod = tryCloneMethod._catch( noSuchMethod );
1114            final JConditional instanceOfSerializable = catchNoSuchMethod.body()._if( o._instanceof( serializable ) );
1115            instanceOfSerializable._then()._return( this.getCopyOfSerializableInvocation( clazz ).
1116                arg( JExpr.cast( serializable, o ) ) );
1117    
1118            catchNoSuchMethod.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1119    
1120            catchNoSuchMethod.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1121                arg( assertionErrorMsg ).invoke( "initCause" ).
1122                arg( catchNoSuchMethod.param( "e" ) ) ) );
1123    
1124            final JCatchBlock catchIllegalAccess = tryCloneMethod._catch( illegalAccess );
1125            catchIllegalAccess.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1126    
1127            catchIllegalAccess.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1128                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchIllegalAccess.param( "e" ) ) ) );
1129    
1130            final JCatchBlock catchInvocationTarget = tryCloneMethod._catch( invocationTarget );
1131            catchInvocationTarget.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1132    
1133            catchInvocationTarget.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1134                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchInvocationTarget.param( "e" ) ) ) );
1135    
1136            final JCatchBlock catchSecurityException = tryCloneMethod._catch( securityException );
1137            catchSecurityException.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1138    
1139            catchSecurityException.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1140                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchSecurityException.param( "e" ) ) ) );
1141    
1142            final JCatchBlock catchIllegalArgument = tryCloneMethod._catch( illegalArgument );
1143            catchIllegalArgument.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1144    
1145            catchIllegalArgument.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1146                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchIllegalArgument.param( "e" ) ) ) );
1147    
1148            final JCatchBlock catchInitializerError = tryCloneMethod._catch( initializerError );
1149            catchInitializerError.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1150    
1151            catchInitializerError.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1152                arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchInitializerError.param( "e" ) ) ) );
1153    
1154            copyBlock._return( JExpr._null() );
1155    
1156            if ( !exceptions.isEmpty() )
1157            {
1158                final JTryBlock tryCopy = m.body()._try();
1159                tryCopy.body().add( copyBlock );
1160    
1161                for ( Class<?> e : exceptions )
1162                {
1163                    final JCatchBlock catchBlock = tryCopy._catch( clazz.parent().getCodeModel().ref( e ) );
1164                    catchBlock.body()._throw( JExpr.cast( assertionError, JExpr._new( assertionError ).
1165                        arg( assertionErrorMsg ).invoke( "initCause" ).arg( catchBlock.param( "e" ) ) ) );
1166    
1167                }
1168            }
1169            else
1170            {
1171                m.body().add( copyBlock );
1172            }
1173    
1174            this.methodCount = this.methodCount.add( BigInteger.ONE );
1175            return ( mod != JMod.PRIVATE ? clazz._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
1176        }
1177    
1178        private JInvocation getCopyOfElementInfoInvocation( final FieldOutline fieldOutline, final CElementInfo element )
1179        {
1180            final JType elementType = element.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION );
1181            final JType[] signature =
1182            {
1183                elementType
1184            };
1185    
1186            final String methodName;
1187            if ( element.hasClass() )
1188            {
1189                methodName = "copyOf" + element.shortName();
1190            }
1191            else
1192            {
1193                methodName = "copyOf" + this.getMethodNamePart(
1194                    element.getContentType().toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ) ) + "Element";
1195    
1196            }
1197    
1198            final int mod = this.getVisibilityModifier();
1199    
1200            if ( mod != JMod.PRIVATE )
1201            {
1202                for ( JMethod m : fieldOutline.parent()._package().objectFactory().methods() )
1203                {
1204                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
1205                    {
1206                        return fieldOutline.parent()._package().objectFactory().staticInvoke( m );
1207                    }
1208                }
1209            }
1210            else
1211            {
1212                for ( JMethod m : fieldOutline.parent().implClass.methods() )
1213                {
1214                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
1215                    {
1216                        return JExpr.invoke( m );
1217                    }
1218                }
1219            }
1220    
1221            final JMethod m =
1222                ( mod != JMod.PRIVATE
1223                  ? fieldOutline.parent()._package().objectFactory().method( JMod.STATIC | mod, elementType, methodName )
1224                  : fieldOutline.parent().implClass.method( JMod.STATIC | mod, elementType, methodName ) );
1225    
1226            final JVar e = m.param( JMod.FINAL, elementType, "e" );
1227    
1228            m.javadoc().append( "Creates and returns a deep copy of a given {@code " + elementType.binaryName()
1229                                + "} instance." );
1230    
1231            m.javadoc().addParam( e ).append( "The instance to copy or {@code null}." );
1232            m.javadoc().addReturn().append( "A deep copy of {@code e} or {@code null} if {@code e} is {@code null}." );
1233    
1234            m.body().directStatement( "// " + getMessage( "title" ) );
1235    
1236            final JConditional elementNotNull = m.body()._if( e.ne( JExpr._null() ) );
1237    
1238            final JExpression newElement;
1239            if ( element.hasClass() )
1240            {
1241                newElement = JExpr._new( elementType ).arg( this.getCopyExpression(
1242                    fieldOutline, element.getContentType(), elementNotNull._then(),
1243                    JExpr.cast( element.getContentType().toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ),
1244                                JExpr.invoke( e, "getValue" ) ), true ) );
1245    
1246            }
1247            else
1248            {
1249                newElement = JExpr._new( elementType ).
1250                    arg( JExpr.invoke( e, "getName" ) ).
1251                    arg( JExpr.invoke( e, "getDeclaredType" ) ).
1252                    arg( JExpr.invoke( e, "getScope" ) ).
1253                    arg( JExpr.invoke( e, "getValue" ) );
1254    
1255            }
1256    
1257            final JVar copy = elementNotNull._then().decl( JMod.FINAL, elementType, "copy", newElement );
1258            elementNotNull._then().add( copy.invoke( "setNil" ).arg( e.invoke( "isNil" ) ) );
1259    
1260            if ( !element.hasClass() )
1261            {
1262                elementNotNull._then().add( copy.invoke( "setValue" ).arg( this.getCopyExpression(
1263                    fieldOutline, element.getContentType(), elementNotNull._then(),
1264                    JExpr.cast( element.getContentType().toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ),
1265                                copy.invoke( "getValue" ) ), true ) ) );
1266    
1267            }
1268    
1269            elementNotNull._then()._return( copy );
1270            m.body()._return( JExpr._null() );
1271            this.methodCount = this.methodCount.add( BigInteger.ONE );
1272            return ( mod != JMod.PRIVATE
1273                     ? fieldOutline.parent()._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
1274    
1275        }
1276    
1277        private JInvocation getCopyOfArrayInfoInvocation( final FieldOutline fieldOutline, final CArrayInfo array )
1278        {
1279            final JType arrayType =
1280                ( array.getAdapterUse() != null && array.getAdapterUse().customType != null
1281                  ? array.getAdapterUse().customType.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION )
1282                  : array.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ) );
1283    
1284            final JType itemType = array.getItemType().toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION );
1285            final JType[] signature =
1286            {
1287                arrayType
1288            };
1289    
1290            final String methodName = "copyOf" + fieldOutline.getPropertyInfo().getName( true );
1291            final int mod = this.getVisibilityModifier();
1292    
1293            if ( mod != JMod.PRIVATE )
1294            {
1295                for ( JMethod m : fieldOutline.parent()._package().objectFactory().methods() )
1296                {
1297                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
1298                    {
1299                        return fieldOutline.parent()._package().objectFactory().staticInvoke( m );
1300                    }
1301                }
1302            }
1303            else
1304            {
1305                for ( JMethod m : fieldOutline.parent().implClass.methods() )
1306                {
1307                    if ( m.name().equals( methodName ) && m.hasSignature( signature ) )
1308                    {
1309                        return JExpr.invoke( m );
1310                    }
1311                }
1312            }
1313    
1314            final JMethod m =
1315                ( mod != JMod.PRIVATE
1316                  ? fieldOutline.parent()._package().objectFactory().method( JMod.STATIC | mod, arrayType, methodName )
1317                  : fieldOutline.parent().implClass.method( JMod.STATIC | mod, arrayType, methodName ) );
1318    
1319            final JVar a = m.param( JMod.FINAL, arrayType, "array" );
1320    
1321            m.javadoc().append(
1322                "Creates and returns a deep copy of a given {@code " + arrayType.binaryName() + "} instance." );
1323    
1324            m.javadoc().addParam( a ).append( "The instance to copy or {@code null}." );
1325            m.javadoc().addReturn().append(
1326                "A deep copy of {@code array} or {@code null} if {@code array} is {@code null}." );
1327    
1328            m.body().directStatement( "// " + getMessage( "title" ) );
1329    
1330            final JConditional arrayNotNull = m.body()._if( a.ne( JExpr._null() ) );
1331            final JVar copy = arrayNotNull._then().decl( arrayType, "copy", JExpr.newArray( itemType, a.ref( "length" ) ) );
1332            final JForLoop forEachItem = arrayNotNull._then()._for();
1333            final JVar i = forEachItem.init(
1334                fieldOutline.parent().parent().getCodeModel().INT, "i", a.ref( "length" ).minus( JExpr.lit( 1 ) ) );
1335    
1336            forEachItem.test( i.gte( JExpr.lit( 0 ) ) );
1337            forEachItem.update( i.decr() );
1338    
1339            final JExpression copyExpr =
1340                this.getCopyExpression( fieldOutline, array.getItemType(), forEachItem.body(), a.component( i ), true );
1341    
1342            forEachItem.body().assign( copy.component( i ), copyExpr );
1343            arrayNotNull._then()._return( copy );
1344            m.body()._return( JExpr._null() );
1345            this.methodCount = this.methodCount.add( BigInteger.ONE );
1346            return ( mod != JMod.PRIVATE
1347                     ? fieldOutline.parent()._package().objectFactory().staticInvoke( m ) : JExpr.invoke( m ) );
1348    
1349        }
1350    
1351        private JMethod getCopyOfPropertyMethod( final FieldOutline field )
1352        {
1353            final String methodName = "copy" + field.getPropertyInfo().getName( true );
1354            for ( JMethod m : field.parent().implClass.methods() )
1355            {
1356                if ( m.name().equals( methodName ) )
1357                {
1358                    return m;
1359                }
1360            }
1361    
1362            final JClass jaxbElement = field.parent().parent().getCodeModel().ref( JAXBElement.class );
1363            final JClass assertionError = field.parent().parent().getCodeModel().ref( AssertionError.class );
1364            final JMethod m = field.parent().implClass.method(
1365                this.getVisibilityModifier() | JMod.STATIC, field.getRawType(), methodName );
1366    
1367            final JVar source = m.param( JMod.FINAL, field.getRawType(), "source" );
1368    
1369            m.javadoc().append( "Creates and returns a deep copy of property {@code "
1370                                + field.getPropertyInfo().getName( true ) + "}." );
1371    
1372            m.javadoc().addParam( source ).append( "The source to copy from or {@code null}." );
1373            m.javadoc().addReturn().append(
1374                "A deep copy of {@code source} or {@code null} if {@code source} is {@code null}." );
1375    
1376            m.body().directStatement( "// " + getMessage( "title" ) );
1377    
1378            final JConditional sourceNotNull = m.body()._if( source.ne( JExpr._null() ) );
1379    
1380    //        m.body()._if( source.eq( JExpr._null() ) )._then()._throw( JExpr._new( nullPointerException ).arg( "source" ) );
1381    //        m.body()._if( target.eq( JExpr._null() ) )._then()._throw( JExpr._new( nullPointerException ).arg( "target" ) );
1382    
1383            final List<CClassInfo> referencedClassInfos =
1384                new ArrayList<CClassInfo>( field.getPropertyInfo().ref().size() );
1385    
1386            final List<CElementInfo> referencedElementInfos =
1387                new ArrayList<CElementInfo>( field.getPropertyInfo().ref().size() );
1388    
1389            final List<CElementInfo> referencedElementInfosWithClass =
1390                new ArrayList<CElementInfo>( field.getPropertyInfo().ref().size() );
1391    
1392            final List<CTypeInfo> referencedTypeInfos =
1393                new ArrayList<CTypeInfo>( field.getPropertyInfo().ref().size() );
1394    
1395            final List<JType> referencedClassTypes =
1396                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1397    
1398            final List<JType> referencedContentTypes =
1399                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1400    
1401            final List<JType> referencedTypes =
1402                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1403    
1404            for ( CTypeInfo type : field.getPropertyInfo().ref() )
1405            {
1406                if ( type instanceof CElementInfo )
1407                {
1408                    final CElementInfo e = (CElementInfo) type;
1409                    if ( e.hasClass() )
1410                    {
1411                        referencedElementInfosWithClass.add( e );
1412                    }
1413                    else
1414                    {
1415                        final JType contentType =
1416                            e.getContentType().toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1417    
1418                        if ( !referencedContentTypes.contains( contentType ) )
1419                        {
1420                            referencedContentTypes.add( contentType );
1421                            referencedElementInfos.add( e );
1422                        }
1423                    }
1424                }
1425                else if ( type instanceof CClassInfo )
1426                {
1427                    final CClassInfo c = (CClassInfo) type;
1428                    final JClass classType = c.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1429    
1430                    if ( !referencedClassTypes.contains( classType ) )
1431                    {
1432                        referencedClassTypes.add( classType );
1433                        referencedClassInfos.add( c );
1434                    }
1435                }
1436                else
1437                {
1438                    final JType javaType = type.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1439                    if ( !referencedTypes.contains( javaType ) )
1440                    {
1441                        referencedTypes.add( javaType );
1442                        referencedTypeInfos.add( type );
1443                    }
1444                }
1445            }
1446    
1447            Collections.sort( referencedClassInfos, new CClassInfoComparator( field.parent().parent() ) );
1448            Collections.sort( referencedElementInfos, new CElementInfoComparator( field.parent().parent(), false ) );
1449            Collections.sort( referencedElementInfosWithClass, new CElementInfoComparator( field.parent().parent(), true ) );
1450            Collections.sort( referencedTypeInfos, new CTypeInfoComparator( field.parent().parent() ) );
1451            Collections.reverse( referencedClassInfos );
1452            Collections.reverse( referencedElementInfos );
1453            Collections.reverse( referencedElementInfosWithClass );
1454            Collections.reverse( referencedTypeInfos );
1455    
1456            if ( !( referencedElementInfos.isEmpty() && referencedElementInfosWithClass.isEmpty() ) )
1457            {
1458                final JBlock elementBlock = sourceNotNull._then()._if( source._instanceof( jaxbElement ) )._then();
1459                if ( !referencedElementInfosWithClass.isEmpty() )
1460                {
1461                    elementBlock.directStatement( "// Referenced elements with classes." );
1462                    for ( CElementInfo elementInfo : referencedElementInfosWithClass )
1463                    {
1464                        final JType elementType = elementInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1465                        final JConditional ifInstanceOf = elementBlock._if( source._instanceof( elementType ) );
1466                        final JExpression copyExpr = this.getCopyExpression(
1467                            field, elementInfo, ifInstanceOf._then(), JExpr.cast( elementType, source ), false );
1468    
1469                        if ( copyExpr == null )
1470                        {
1471                            this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1472                                      field.parent().implClass.binaryName() );
1473    
1474                        }
1475                        else
1476                        {
1477                            ifInstanceOf._then()._return( copyExpr );
1478                        }
1479                    }
1480                }
1481    
1482                if ( !referencedElementInfos.isEmpty() )
1483                {
1484                    elementBlock.directStatement( "// Referenced elements without classes." );
1485                    for ( CElementInfo elementInfo : referencedElementInfos )
1486                    {
1487                        final JType contentType =
1488                            ( elementInfo.getAdapterUse() != null && elementInfo.getAdapterUse().customType != null
1489                              ? elementInfo.getAdapterUse().customType.toType( field.parent().parent(),
1490                                                                               Aspect.IMPLEMENTATION )
1491                              : elementInfo.getContentType().toType( field.parent().parent(), Aspect.IMPLEMENTATION ) );
1492    
1493                        final JConditional ifInstanceOf = elementBlock._if( JExpr.invoke( JExpr.cast(
1494                            jaxbElement, source ), "getValue" )._instanceof( contentType ) );
1495    
1496                        final JExpression copyExpr = this.getCopyExpression(
1497                            field, elementInfo, ifInstanceOf._then(), JExpr.cast( jaxbElement, source ), false );
1498    
1499                        if ( copyExpr == null )
1500                        {
1501                            this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1502                                      field.parent().implClass.binaryName() );
1503    
1504                        }
1505                        else
1506                        {
1507                            ifInstanceOf._then()._return( copyExpr );
1508                        }
1509                    }
1510                }
1511            }
1512    
1513            for ( CClassInfo classInfo : referencedClassInfos )
1514            {
1515                final JType javaType =
1516                    ( classInfo.getAdapterUse() != null && classInfo.getAdapterUse().customType != null
1517                      ? classInfo.getAdapterUse().customType.toType( field.parent().parent(), Aspect.IMPLEMENTATION )
1518                      : classInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION ) );
1519    
1520                final JConditional ifInstanceOf = sourceNotNull._then()._if( source._instanceof( javaType ) );
1521    
1522                final JExpression copyExpr =
1523                    this.getCopyExpression( field, classInfo, ifInstanceOf._then(), JExpr.cast( javaType, source ), false );
1524    
1525                if ( copyExpr == null )
1526                {
1527                    this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1528                              field.parent().implClass.binaryName() );
1529    
1530                }
1531                else
1532                {
1533                    ifInstanceOf._then()._return( copyExpr );
1534                }
1535            }
1536    
1537            for ( CTypeInfo typeInfo : referencedTypeInfos )
1538            {
1539                final JType javaType = typeInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1540                final JConditional ifInstanceOf = sourceNotNull._then()._if( source._instanceof( javaType ) );
1541                final JExpression copyExpr =
1542                    this.getCopyExpression( field, typeInfo, ifInstanceOf._then(), JExpr.cast( javaType, source ), false );
1543    
1544                if ( copyExpr == null )
1545                {
1546                    this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1547                              field.parent().implClass.binaryName() );
1548    
1549                }
1550                else
1551                {
1552                    ifInstanceOf._then()._return( copyExpr );
1553                }
1554            }
1555    
1556            sourceNotNull._then().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1557            sourceNotNull._then()._throw( JExpr._new( assertionError ).arg( JExpr.lit( "Unexpected instance '" ).
1558                plus( source ).plus( JExpr.lit( "' for property '" + field.getPropertyInfo().getName( true )
1559                                                + "' of class '" + field.parent().implClass.binaryName() + "'." ) ) ) );
1560    
1561            m.body()._return( JExpr._null() );
1562            this.methodCount = this.methodCount.add( BigInteger.ONE );
1563            return m;
1564        }
1565    
1566        private JMethod getCopyOfCollectionMethod( final FieldOutline field )
1567        {
1568            final String methodName = "copy" + field.getPropertyInfo().getName( true );
1569            for ( JMethod m : field.parent().implClass.methods() )
1570            {
1571                if ( m.name().equals( methodName ) )
1572                {
1573                    return m;
1574                }
1575            }
1576    
1577            final JClass object = field.parent().parent().getCodeModel().ref( Object.class );
1578            final JClass array = field.parent().parent().getCodeModel().ref( Array.class );
1579            final JClass jaxbElement = field.parent().parent().getCodeModel().ref( JAXBElement.class );
1580            final JClass nullPointerException = field.parent().parent().getCodeModel().ref( NullPointerException.class );
1581            final JClass assertionError = field.parent().parent().getCodeModel().ref( AssertionError.class );
1582            final JMethod m = field.parent().implClass.method(
1583                field.getRawType().isArray() ? this.getVisibilityModifier() : this.getVisibilityModifier() | JMod.STATIC,
1584                Void.TYPE, methodName );
1585    
1586            final JVar source = m.param( JMod.FINAL, field.getRawType(), "source" );
1587            final JVar target = field.getRawType().isArray() ? null : m.param( JMod.FINAL, field.getRawType(), "target" );
1588    
1589            m.javadoc().append( "Copies all values of property {@code " + field.getPropertyInfo().getName( true )
1590                                + "} deeply." );
1591    
1592            m.javadoc().addParam( source ).append( "The source to copy from." );
1593    
1594            if ( !field.getRawType().isArray() )
1595            {
1596                m.javadoc().addParam( target ).append( "The target to copy {@code source} to." );
1597                m.javadoc().addThrows( nullPointerException ).
1598                    append( "if {@code source} or {@code target} is {@code null}." );
1599    
1600                m.annotate( SuppressWarnings.class ).param( "value", "unchecked" );
1601            }
1602            else
1603            {
1604                m.javadoc().addThrows( nullPointerException ).append( "if {@code source} is {@code null}." );
1605            }
1606    
1607            m.body().directStatement( "// " + getMessage( "title" ) );
1608    
1609    //        m.body()._if( source.eq( JExpr._null() ) )._then()._throw( JExpr._new( nullPointerException ).arg( "source" ) );
1610    //        m.body()._if( target.eq( JExpr._null() ) )._then()._throw( JExpr._new( nullPointerException ).arg( "target" ) );
1611    
1612            final List<CClassInfo> referencedClassInfos =
1613                new ArrayList<CClassInfo>( field.getPropertyInfo().ref().size() );
1614    
1615            final List<CElementInfo> referencedElementInfos =
1616                new ArrayList<CElementInfo>( field.getPropertyInfo().ref().size() );
1617    
1618            final List<CElementInfo> referencedElementInfosWithClass =
1619                new ArrayList<CElementInfo>( field.getPropertyInfo().ref().size() );
1620    
1621            final List<CTypeInfo> referencedTypeInfos =
1622                new ArrayList<CTypeInfo>( field.getPropertyInfo().ref().size() );
1623    
1624            final List<JType> referencedClassTypes =
1625                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1626    
1627            final List<JType> referencedContentTypes =
1628                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1629    
1630            final List<JType> referencedTypes =
1631                new ArrayList<JType>( field.getPropertyInfo().ref().size() );
1632    
1633            for ( CTypeInfo type : field.getPropertyInfo().ref() )
1634            {
1635                if ( type instanceof CElementInfo )
1636                {
1637                    final CElementInfo e = (CElementInfo) type;
1638                    if ( e.hasClass() )
1639                    {
1640                        referencedElementInfosWithClass.add( e );
1641                    }
1642                    else
1643                    {
1644                        final JType contentType =
1645                            e.getContentType().toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1646    
1647                        if ( !referencedContentTypes.contains( contentType ) )
1648                        {
1649                            referencedContentTypes.add( contentType );
1650                            referencedElementInfos.add( e );
1651                        }
1652                    }
1653                }
1654                else if ( type instanceof CClassInfo )
1655                {
1656                    final CClassInfo c = (CClassInfo) type;
1657                    final JClass classType = c.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1658    
1659                    if ( !referencedClassTypes.contains( classType ) )
1660                    {
1661                        referencedClassTypes.add( classType );
1662                        referencedClassInfos.add( c );
1663                    }
1664                }
1665                else
1666                {
1667                    final JType javaType = type.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1668                    if ( !referencedTypes.contains( javaType ) )
1669                    {
1670                        referencedTypes.add( javaType );
1671                        referencedTypeInfos.add( type );
1672                    }
1673                }
1674            }
1675    
1676            Collections.sort( referencedClassInfos, new CClassInfoComparator( field.parent().parent() ) );
1677            Collections.sort( referencedElementInfos, new CElementInfoComparator( field.parent().parent(), false ) );
1678            Collections.sort( referencedElementInfosWithClass, new CElementInfoComparator( field.parent().parent(), true ) );
1679            Collections.sort( referencedTypeInfos, new CTypeInfoComparator( field.parent().parent() ) );
1680            Collections.reverse( referencedClassInfos );
1681            Collections.reverse( referencedElementInfos );
1682            Collections.reverse( referencedElementInfosWithClass );
1683            Collections.reverse( referencedTypeInfos );
1684    
1685            final JForLoop copyLoop;
1686            final JVar it;
1687            final JVar next;
1688            final JVar copy;
1689            final JConditional sourceNotEmpty;
1690    
1691            if ( field.getRawType().isArray() )
1692            {
1693                sourceNotEmpty =
1694                    m.body()._if( source.ne( JExpr._null() ).cand( source.ref( "length" ).gt( JExpr.lit( 0 ) ) ) );
1695    
1696                copy = sourceNotEmpty._then().decl( JMod.FINAL, source.type(), "copy", JExpr.cast(
1697                    source.type(), array.staticInvoke( "newInstance" ).
1698                    arg( source.invoke( "getClass" ).invoke( "getComponentType" ) ).arg( source.ref( "length" ) ) ) );
1699    
1700                copyLoop = sourceNotEmpty._then()._for();
1701                it = copyLoop.init( field.parent().parent().getCodeModel().INT, "i",
1702                                    source.ref( "length" ).minus( JExpr.lit( 1 ) ) );
1703    
1704                copyLoop.test( it.gte( JExpr.lit( 0 ) ) );
1705                copyLoop.update( it.decr() );
1706                next = copyLoop.body().decl( JMod.FINAL, object, "next", source.component( it ) );
1707            }
1708            else
1709            {
1710                sourceNotEmpty = m.body()._if( JExpr.invoke( source, "isEmpty" ).not() );
1711                copyLoop = sourceNotEmpty._then()._for();
1712                it = copyLoop.init( field.parent().parent().getCodeModel().ref( Iterator.class ),
1713                                    "it", source.invoke( "iterator" ) );
1714    
1715                copyLoop.test( JExpr.invoke( it, "hasNext" ) );
1716                next = copyLoop.body().decl( JMod.FINAL, object, "next", JExpr.invoke( it, "next" ) );
1717                copy = null;
1718            }
1719    
1720            if ( !( referencedElementInfos.isEmpty() && referencedElementInfosWithClass.isEmpty() ) )
1721            {
1722                final JBlock copyBlock = copyLoop.body()._if( next._instanceof( jaxbElement ) )._then();
1723                if ( !referencedElementInfosWithClass.isEmpty() )
1724                {
1725                    copyBlock.directStatement( "// Referenced elements with classes." );
1726                    for ( CElementInfo elementInfo : referencedElementInfosWithClass )
1727                    {
1728                        final JType elementType = elementInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1729                        final JConditional ifInstanceOf = copyBlock._if( next._instanceof( elementType ) );
1730                        final JExpression copyExpr = this.getCopyExpression(
1731                            field, elementInfo, ifInstanceOf._then(), JExpr.cast( elementType, next ), false );
1732    
1733                        if ( copyExpr == null )
1734                        {
1735                            this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1736                                      field.parent().implClass.binaryName() );
1737    
1738                        }
1739                        else
1740                        {
1741                            if ( field.getRawType().isArray() )
1742                            {
1743                                ifInstanceOf._then().assign( copy.component( it ), copyExpr );
1744                            }
1745                            else
1746                            {
1747                                ifInstanceOf._then().invoke( target, "add" ).arg( copyExpr );
1748                            }
1749    
1750                            ifInstanceOf._then()._continue();
1751                        }
1752                    }
1753                }
1754    
1755                if ( !referencedElementInfos.isEmpty() )
1756                {
1757                    copyBlock.directStatement( "// Referenced elements without classes." );
1758                    for ( CElementInfo elementInfo : referencedElementInfos )
1759                    {
1760                        final JType contentType =
1761                            ( elementInfo.getAdapterUse() != null && elementInfo.getAdapterUse().customType != null
1762                              ? elementInfo.getAdapterUse().customType.toType( field.parent().parent(),
1763                                                                               Aspect.IMPLEMENTATION )
1764                              : elementInfo.getContentType().toType( field.parent().parent(), Aspect.IMPLEMENTATION ) );
1765    
1766                        final JConditional ifInstanceOf = copyBlock._if( JExpr.invoke( JExpr.cast(
1767                            jaxbElement, next ), "getValue" )._instanceof( contentType ) );
1768    
1769                        final JExpression copyExpr =
1770                            this.getCopyExpression( field, elementInfo, ifInstanceOf._then(),
1771                                                    JExpr.cast( jaxbElement, next ), false );
1772    
1773                        if ( copyExpr == null )
1774                        {
1775                            this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1776                                      field.parent().implClass.binaryName() );
1777    
1778                        }
1779                        else
1780                        {
1781                            if ( field.getRawType().isArray() )
1782                            {
1783                                ifInstanceOf._then().assign( copy.component( it ), copyExpr );
1784                            }
1785                            else
1786                            {
1787                                ifInstanceOf._then().invoke( target, "add" ).arg( copyExpr );
1788                            }
1789                        }
1790    
1791                        ifInstanceOf._then()._continue();
1792                    }
1793                }
1794            }
1795    
1796            for ( CClassInfo classInfo : referencedClassInfos )
1797            {
1798                final JType javaType =
1799                    ( classInfo.getAdapterUse() != null && classInfo.getAdapterUse().customType != null
1800                      ? classInfo.getAdapterUse().customType.toType( field.parent().parent(), Aspect.IMPLEMENTATION )
1801                      : classInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION ) );
1802    
1803                final JConditional ifInstanceOf = copyLoop.body()._if( next._instanceof( javaType ) );
1804    
1805                final JExpression copyExpr =
1806                    this.getCopyExpression( field, classInfo, ifInstanceOf._then(), JExpr.cast( javaType, next ), false );
1807    
1808                if ( copyExpr == null )
1809                {
1810                    this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1811                              field.parent().implClass.binaryName() );
1812    
1813                }
1814                else
1815                {
1816                    if ( field.getRawType().isArray() )
1817                    {
1818                        ifInstanceOf._then().assign( copy.component( it ), copyExpr );
1819                    }
1820                    else
1821                    {
1822                        ifInstanceOf._then().invoke( target, "add" ).arg( copyExpr );
1823                    }
1824                }
1825    
1826                ifInstanceOf._then()._continue();
1827            }
1828    
1829            for ( CTypeInfo typeInfo : referencedTypeInfos )
1830            {
1831                final JType javaType = typeInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
1832                final JConditional ifInstanceOf = copyLoop.body()._if( next._instanceof( javaType ) );
1833                final JExpression copyExpr =
1834                    this.getCopyExpression( field, typeInfo, ifInstanceOf._then(), JExpr.cast( javaType, next ), false );
1835    
1836                if ( copyExpr == null )
1837                {
1838                    this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
1839                              field.parent().implClass.binaryName() );
1840    
1841                }
1842                else
1843                {
1844                    if ( field.getRawType().isArray() )
1845                    {
1846                        ifInstanceOf._then().assign( copy.component( it ), copyExpr );
1847                    }
1848                    else
1849                    {
1850                        ifInstanceOf._then().invoke( target, "add" ).arg( copyExpr );
1851                    }
1852                }
1853    
1854                ifInstanceOf._then()._continue();
1855            }
1856    
1857            copyLoop.body().directStatement( "// Please report this at " + getMessage( "bugtrackerUrl" ) );
1858            copyLoop.body()._throw( JExpr._new( assertionError ).arg( JExpr.lit( "Unexpected instance '" ).plus(
1859                next ).plus( JExpr.lit( "' for property '" + field.getPropertyInfo().getName( true ) + "' of class '"
1860                                        + field.parent().implClass.binaryName() + "'." ) ) ) );
1861    
1862            if ( field.getRawType().isArray() )
1863            {
1864                sourceNotEmpty._then().add( JExpr.invoke( "set" + field.getPropertyInfo().getName( true ) ).arg( copy ) );
1865            }
1866    
1867            this.methodCount = this.methodCount.add( BigInteger.ONE );
1868            return m;
1869        }
1870    
1871        private JExpression getCopyExpression( final FieldOutline fieldOutline, final CTypeInfo type,
1872                                               final JBlock block, final JExpression source,
1873                                               final boolean sourceMaybeNull )
1874        {
1875            JExpression expr = null;
1876    
1877            if ( type instanceof CBuiltinLeafInfo )
1878            {
1879                expr = this.getBuiltinCopyExpression(
1880                    fieldOutline, (CBuiltinLeafInfo) type, block, source, sourceMaybeNull );
1881    
1882            }
1883            else if ( type instanceof CWildcardTypeInfo )
1884            {
1885                expr = this.getWildcardCopyExpression(
1886                    fieldOutline, (CWildcardTypeInfo) type, block, source, sourceMaybeNull );
1887    
1888            }
1889            else if ( type instanceof CClassInfo )
1890            {
1891                expr = this.getClassInfoCopyExpression( fieldOutline, (CClassInfo) type, block, source, sourceMaybeNull );
1892            }
1893            else if ( type instanceof CEnumLeafInfo )
1894            {
1895                expr = this.getEnumLeafInfoCopyExpression( fieldOutline, (CEnumLeafInfo) type, block, source );
1896            }
1897            else if ( type instanceof CArrayInfo )
1898            {
1899                expr = this.getArrayCopyExpression( fieldOutline, (CArrayInfo) type, block, source );
1900            }
1901            else if ( type instanceof CElementInfo )
1902            {
1903                expr = this.getElementCopyExpression( fieldOutline, (CElementInfo) type, block, source );
1904            }
1905            else if ( type instanceof CNonElement )
1906            {
1907                expr = this.getNonElementCopyExpression( fieldOutline, (CNonElement) type, block, source, sourceMaybeNull );
1908            }
1909            else if ( type instanceof CAdapterInfo )
1910            {
1911                expr = this.getAdapterInfoCopyExpression( fieldOutline, (CAdapterInfo) type, block, source );
1912            }
1913    
1914            if ( expr != null )
1915            {
1916                this.expressionCount = this.expressionCount.add( BigInteger.ONE );
1917            }
1918    
1919            return expr;
1920        }
1921    
1922        private JExpression getBuiltinCopyExpression( final FieldOutline fieldOutline, final CBuiltinLeafInfo type,
1923                                                      final JBlock block, final JExpression source,
1924                                                      final boolean sourceMaybeNull )
1925        {
1926            JExpression expr = null;
1927    
1928            block.directStatement( "// CBuiltinLeafInfo: " + type.toType( fieldOutline.parent().parent(),
1929                                                                          Aspect.IMPLEMENTATION ).binaryName() );
1930    
1931            if ( type == CBuiltinLeafInfo.ANYTYPE )
1932            {
1933                expr = this.getCopyOfObjectInvocation( fieldOutline.parent() ).arg( source );
1934            }
1935            else if ( type == CBuiltinLeafInfo.BASE64_BYTE_ARRAY )
1936            {
1937                final JClass byteArray = fieldOutline.parent().parent().getCodeModel().ref( byte[].class );
1938                expr = this.getCopyOfPrimitiveArrayExpression( fieldOutline.parent(), byteArray, source );
1939            }
1940            else if ( type == CBuiltinLeafInfo.BIG_DECIMAL || type == CBuiltinLeafInfo.BIG_INTEGER
1941                      || type == CBuiltinLeafInfo.STRING || type == CBuiltinLeafInfo.BOOLEAN || type == CBuiltinLeafInfo.INT
1942                      || type == CBuiltinLeafInfo.LONG || type == CBuiltinLeafInfo.BYTE || type == CBuiltinLeafInfo.SHORT
1943                      || type == CBuiltinLeafInfo.FLOAT || type == CBuiltinLeafInfo.DOUBLE )
1944            {
1945                expr = source;
1946            }
1947            else if ( type == CBuiltinLeafInfo.QNAME )
1948            {
1949                expr = source;
1950            }
1951            else if ( type == CBuiltinLeafInfo.CALENDAR )
1952            {
1953                final JClass xmlCal = fieldOutline.parent().parent().getCodeModel().ref( XMLGregorianCalendar.class );
1954                if ( sourceMaybeNull )
1955                {
1956                    expr = JOp.cond( source.eq( JExpr._null() ), JExpr._null(),
1957                                     JExpr.cast( xmlCal, source.invoke( "clone" ) ) );
1958    
1959                }
1960                else
1961                {
1962                    expr = JExpr.cast( xmlCal, source.invoke( "clone" ) );
1963                }
1964            }
1965            else if ( type == CBuiltinLeafInfo.DURATION )
1966            {
1967                expr = source;
1968            }
1969            else if ( type == CBuiltinLeafInfo.DATA_HANDLER || type == CBuiltinLeafInfo.IMAGE
1970                      || type == CBuiltinLeafInfo.XML_SOURCE )
1971            {
1972                this.log( Level.WARNING, "cannotCopyType",
1973                          type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ).fullName(),
1974                          fieldOutline.getPropertyInfo().getName( true ), fieldOutline.parent().implClass.fullName() );
1975    
1976                expr = source;
1977            }
1978    
1979            return expr;
1980        }
1981    
1982        private JExpression getWildcardCopyExpression( final FieldOutline fieldOutline, final CWildcardTypeInfo type,
1983                                                       final JBlock block, final JExpression source,
1984                                                       final boolean sourceMaybeNull )
1985        {
1986            block.directStatement( "// CWildcardTypeInfo: " + type.toType( fieldOutline.parent().parent(),
1987                                                                           Aspect.IMPLEMENTATION ).binaryName() );
1988    
1989            if ( sourceMaybeNull )
1990            {
1991    
1992                return JOp.cond( source.eq( JExpr._null() ), JExpr._null(),
1993                                 JExpr.cast( fieldOutline.parent().parent().getCodeModel().ref( Element.class ),
1994                                             source.invoke( "cloneNode" ).arg( JExpr.TRUE ) ) );
1995    
1996            }
1997            else
1998            {
1999                return JExpr.cast( fieldOutline.parent().parent().getCodeModel().ref( Element.class ),
2000                                   source.invoke( "cloneNode" ).arg( JExpr.TRUE ) );
2001    
2002            }
2003        }
2004    
2005        private JExpression getClassInfoCopyExpression( final FieldOutline fieldOutline, final CClassInfo type,
2006                                                        final JBlock block, final JExpression source,
2007                                                        final boolean sourceMaybeNull )
2008        {
2009            block.directStatement(
2010                "// CClassInfo: " + type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ).binaryName() );
2011    
2012            if ( sourceMaybeNull )
2013            {
2014                return JOp.cond( source.eq( JExpr._null() ), JExpr._null(), source.invoke( "clone" ) );
2015            }
2016            else
2017            {
2018                return source.invoke( "clone" );
2019            }
2020        }
2021    
2022        private JExpression getNonElementCopyExpression( final FieldOutline fieldOutline, final CNonElement type,
2023                                                         final JBlock block, final JExpression source,
2024                                                         final boolean sourceMaybeNull )
2025        {
2026            final JType jType = type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION );
2027            block.directStatement( "// CNonElement: " + jType.binaryName() );
2028    
2029            block.directStatement( "// " + WARNING_PREFIX + ": " + "The '" + jType.binaryName() + "'" );
2030            block.directStatement( "// " + WARNING_PREFIX + ": type was not part of the compilation unit." );
2031    
2032            block.directStatement( "// " + WARNING_PREFIX + ": "
2033                                   + "The plugin assumes that type to declare a 'public Object clone()' method which " );
2034    
2035            block.directStatement( "// " + WARNING_PREFIX + ": "
2036                                   + "does not throw a 'CloneNotSupportedException' and to directly extend class" );
2037    
2038            block.directStatement( "// " + WARNING_PREFIX + ": "
2039                                   + "'java.lang.Object'. If this warning is part of an 'if instanceof' block," );
2040    
2041            block.directStatement( "// " + WARNING_PREFIX + ": "
2042                                   + "the order of 'if instanceof' statements may be wrong and must be verified." );
2043    
2044            this.log( Level.WARNING, "nonElementWarning",
2045                      fieldOutline.parent().implClass.fullName(), fieldOutline.getPropertyInfo().getName( true ),
2046                      jType.binaryName(), WARNING_PREFIX );
2047    
2048            if ( sourceMaybeNull )
2049            {
2050                return JOp.cond( source.eq( JExpr._null() ), JExpr._null(), JExpr.cast( jType, source.invoke( "clone" ) ) );
2051            }
2052            else
2053            {
2054                return JExpr.cast( jType, source.invoke( "clone" ) );
2055            }
2056        }
2057    
2058        private JExpression getArrayCopyExpression( final FieldOutline fieldOutline, final CArrayInfo type,
2059                                                    final JBlock block, final JExpression source )
2060        {
2061            block.directStatement( "// CArrayInfo: " + type.fullName() );
2062            return this.getCopyOfArrayInfoInvocation( fieldOutline, type ).arg( source );
2063        }
2064    
2065        private JExpression getElementCopyExpression( final FieldOutline fieldOutline, final CElementInfo type,
2066                                                      final JBlock block, final JExpression source )
2067        {
2068            block.directStatement(
2069                "// CElementInfo: " + type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ).binaryName() );
2070    
2071            return this.getCopyOfElementInfoInvocation( fieldOutline, type ).arg( source );
2072        }
2073    
2074        private JExpression getEnumLeafInfoCopyExpression( final FieldOutline fieldOutline, final CEnumLeafInfo type,
2075                                                           final JBlock block, final JExpression source )
2076        {
2077            block.directStatement(
2078                "// CEnumLeafInfo: " + type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ).binaryName() );
2079    
2080            return source;
2081        }
2082    
2083        private JExpression getAdapterInfoCopyExpression( final FieldOutline fieldOutline, final CAdapterInfo type,
2084                                                          final JBlock block, final JExpression source )
2085        {
2086            block.directStatement( "// CAdapterInfo: "
2087                                   + type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION ).binaryName() );
2088    
2089            final JType jType = type.toType( fieldOutline.parent().parent(), Aspect.IMPLEMENTATION );
2090            return JExpr.cast( jType, this.getCopyOfObjectInvocation( fieldOutline.parent() ).arg( source ) );
2091        }
2092    
2093        private JMethod generateStandardConstructor( final ClassOutline clazz )
2094        {
2095            final JMethod ctor = clazz.implClass.constructor( JMod.PUBLIC );
2096            ctor.body().directStatement( "// " + getMessage( "title" ) );
2097            ctor.body().invoke( "super" );
2098            ctor.javadoc().add( "Creates a new {@code " + clazz.implClass.name() + "} instance." );
2099            this.constructorCount = this.constructorCount.add( BigInteger.ONE );
2100            return ctor;
2101        }
2102    
2103        private JMethod generateCopyConstructor( final ClassOutline clazz )
2104        {
2105            final JMethod ctor = clazz.implClass.constructor( JMod.PUBLIC );
2106            final JClass paramClass = this.hierarchical ? this.getSupertype( clazz.implClass ) : clazz.implClass;
2107            final JVar o = ctor.param( JMod.FINAL, paramClass, "o" );
2108            final boolean superTypeParam = !clazz.implClass.equals( paramClass );
2109    
2110            ctor.javadoc().add( "Creates a new {@code " + clazz.implClass.name()
2111                                + "} instance by deeply copying a given {@code " + paramClass.name() + "} instance.\n" );
2112    
2113            if ( !this.nullable )
2114            {
2115                ctor.javadoc().addParam( o ).add( "The instance to copy." );
2116                ctor.javadoc().addThrows( NullPointerException.class ).append( "if {@code o} is {@code null}." );
2117            }
2118            else
2119            {
2120                ctor.javadoc().addParam( o ).add( "The instance to copy or {@code null}." );
2121            }
2122    
2123            ctor.body().directStatement( "// " + getMessage( "title" ) );
2124    
2125            if ( this.needsWarningOnReferencedSupertypes( clazz ) )
2126            {
2127                ctor.body().directStatement(
2128                    "// " + WARNING_PREFIX + ": A super-class of this class was not part of the compilation unit." );
2129    
2130                ctor.body().directStatement( "// " + WARNING_PREFIX
2131                                             + ": The plugin assumes this super-class to directly extend class "
2132                                             + "'java.lang.Object'." );
2133    
2134                ctor.body().directStatement( "// " + WARNING_PREFIX
2135                                             + ": The type of the constructor arguments (type of o) in the hierarchy "
2136                                             + "this constructor is part" );
2137    
2138                ctor.body().directStatement( "// " + WARNING_PREFIX + ": of may be wrong and must be verified." );
2139            }
2140    
2141            if ( clazz.getSuperClass() != null || ( clazz.implClass._extends() != null && !clazz.implClass._extends().
2142                                                   binaryName().equals( "java.lang.Object" ) ) )
2143            {
2144                ctor.body().invoke( "super" ).arg( o );
2145            }
2146            else
2147            {
2148                ctor.body().invoke( "super" );
2149            }
2150    
2151            if ( !this.nullable )
2152            {
2153                ctor.body()._if( o.eq( JExpr._null() ) )._then()._throw(
2154                    JExpr._new( clazz.parent().getCodeModel().ref( NullPointerException.class ) ).
2155                    arg( "Cannot create a copy of '" + clazz.implClass.name() + "' from 'null'." ) );
2156    
2157            }
2158    
2159            this.contextExceptions.clear();
2160    
2161            boolean hasFields = false;
2162            if ( !clazz.implClass.fields().isEmpty() )
2163            {
2164                final JBlock copyBlock = new JBlock( false, false );
2165                final JExpression source = superTypeParam ? JExpr.cast( clazz.implClass, o ) : o;
2166    
2167                for ( FieldOutline field : clazz.getDeclaredFields() )
2168                {
2169                    hasFields = true;
2170                    this.generateCopyOfProperty( field, source, copyBlock );
2171                }
2172    
2173                for ( JFieldVar field : clazz.implClass.fields().values() )
2174                {
2175                    if ( ( field.mods().getValue() & JMod.STATIC ) == JMod.STATIC )
2176                    {
2177                        continue;
2178                    }
2179    
2180                    hasFields = true;
2181                    final FieldOutline fieldOutline = this.getFieldOutline( clazz, field.name() );
2182                    if ( fieldOutline == null )
2183                    {
2184                        if ( field.type().isPrimitive() )
2185                        {
2186                            copyBlock.directStatement( "// Unknown primitive field '" + field.name() + "'." );
2187                            copyBlock.assign( JExpr.refthis( field.name() ), source.ref( field ) );
2188                            this.log( Level.WARNING, "fieldWithoutProperties", field.name(), clazz.implClass.name() );
2189                        }
2190                        else
2191                        {
2192                            if ( field.name().equals( "otherAttributes" ) && clazz.target.declaresAttributeWildcard() )
2193                            {
2194                                copyBlock.directStatement( "// Other attributes." );
2195                                copyBlock.add(
2196                                    JExpr.refthis( field.name() ).invoke( "putAll" ).arg( source.ref( field ) ) );
2197    
2198                            }
2199                            else
2200                            {
2201                                copyBlock.directStatement( "// Unknown reference field '" + field.name() + "'." );
2202                                copyBlock.assign( JExpr.refthis( field.name() ), JExpr.cast(
2203                                    field.type(), this.getCopyOfObjectInvocation( clazz ).arg( source.ref( field ) ) ) );
2204    
2205                                this.log( Level.WARNING, "fieldWithoutProperties", field.name(), clazz.implClass.name() );
2206                            }
2207                        }
2208                    }
2209                }
2210    
2211                if ( hasFields )
2212                {
2213                    JBlock effective = ctor.body();
2214    
2215                    if ( !this.contextExceptions.isEmpty() )
2216                    {
2217                        final JTryBlock tryCopy = ctor.body()._try();
2218                        effective = tryCopy.body();
2219    
2220                        for ( Class<?> e : this.contextExceptions )
2221                        {
2222                            final JCatchBlock catchBlock = tryCopy._catch( clazz.parent().getCodeModel().ref( e ) );
2223                            catchBlock.body().directStatement(
2224                                "// Please report this at " + getMessage( "bugtrackerUrl" ) );
2225    
2226                            catchBlock.body()._throw( JExpr._new( clazz.parent().getCodeModel().
2227                                ref( AssertionError.class ) ).arg( catchBlock.param( "e" ) ) );
2228    
2229                        }
2230                    }
2231    
2232                    if ( superTypeParam )
2233                    {
2234                        effective._if( o._instanceof( clazz.implClass ) )._then().add( copyBlock );
2235                    }
2236                    else if ( this.nullable )
2237                    {
2238                        effective._if( o.ne( JExpr._null() ) )._then().add( copyBlock );
2239                    }
2240                    else
2241                    {
2242                        effective.add( copyBlock );
2243                    }
2244                }
2245            }
2246    
2247            this.constructorCount = this.constructorCount.add( BigInteger.ONE );
2248            return ctor;
2249        }
2250    
2251        private void warnOnReferencedSupertypes( final ClassOutline clazz )
2252        {
2253            if ( clazz.getSuperClass() == null && ( clazz.implClass._extends() != null && !clazz.implClass._extends().
2254                                                   binaryName().equals( "java.lang.Object" ) ) )
2255            {
2256                this.log( Level.WARNING, "referencedSupertypeWarning", clazz.implClass.fullName(),
2257                          clazz.implClass._extends().binaryName(), WARNING_PREFIX );
2258    
2259            }
2260    
2261            if ( clazz.getSuperClass() != null )
2262            {
2263                this.warnOnReferencedSupertypes( clazz.getSuperClass() );
2264            }
2265        }
2266    
2267        private boolean needsWarningOnReferencedSupertypes( final ClassOutline clazz )
2268        {
2269            if ( clazz.getSuperClass() == null && ( clazz.implClass._extends() != null && !clazz.implClass._extends().
2270                                                   binaryName().equals( "java.lang.Object" ) ) )
2271            {
2272                return true;
2273            }
2274    
2275            if ( clazz.getSuperClass() != null )
2276            {
2277                return this.needsWarningOnReferencedSupertypes( clazz.getSuperClass() );
2278            }
2279    
2280            return false;
2281        }
2282    
2283        private JClass getSupertype( final JClass clazz )
2284        {
2285            if ( clazz._extends() != null && !clazz._extends().binaryName().equals( "java.lang.Object" ) )
2286            {
2287                return this.getSupertype( clazz._extends() );
2288            }
2289    
2290            return clazz;
2291        }
2292    
2293        private JMethod generateCloneMethod( final ClassOutline clazz )
2294        {
2295            JMethod cloneMethod = null;
2296    
2297            if ( clazz.implClass.isAbstract() )
2298            {
2299                cloneMethod = clazz.implClass.method( JMod.ABSTRACT | JMod.PUBLIC, clazz.implClass, "clone" );
2300            }
2301            else
2302            {
2303                cloneMethod = clazz.implClass.method( JMod.PUBLIC, clazz.implClass, "clone" );
2304                cloneMethod.body().directStatement( "// " + getMessage( "title" ) );
2305                cloneMethod.body()._return( JExpr._new( clazz.implClass ).arg( JExpr._this() ) );
2306            }
2307    
2308            cloneMethod.annotate( Override.class );
2309            clazz.implClass._implements( clazz.parent().getCodeModel().ref( Cloneable.class ) );
2310            cloneMethod.javadoc().append( "Creates and returns a deep copy of this object.\n" );
2311            cloneMethod.javadoc().addReturn().append( "A deep copy of this object." );
2312            this.methodCount = this.methodCount.add( BigInteger.ONE );
2313            return cloneMethod;
2314        }
2315    
2316        private void generateCopyOfProperty( final FieldOutline field, final JExpression o, final JBlock block )
2317        {
2318            final JMethod getter = this.getPropertyGetter( field );
2319    
2320            if ( getter != null )
2321            {
2322                if ( field.getPropertyInfo().isCollection() )
2323                {
2324                    if ( field.getRawType().isArray() )
2325                    {
2326                        block.directStatement( "// '" + field.getPropertyInfo().getName( true ) + "' array." );
2327                        final JConditional fieldNotNull =
2328                            block._if( JExpr.ref( o, field.getPropertyInfo().getName( false ) ).ne( JExpr._null() ) );
2329    
2330                        fieldNotNull._then().invoke( this.getCopyOfCollectionMethod( field ) ).
2331                            arg( JExpr.invoke( o, getter ) );
2332    
2333                    }
2334                    else
2335                    {
2336                        block.directStatement( "// '" + field.getPropertyInfo().getName( true ) + "' collection." );
2337                        final JConditional fieldNotNull =
2338                            block._if( JExpr.ref( o, field.getPropertyInfo().getName( false ) ).ne( JExpr._null() ) );
2339    
2340                        fieldNotNull._then().invoke( this.getCopyOfCollectionMethod( field ) ).
2341                            arg( JExpr.invoke( o, getter ) ).arg( JExpr.invoke( getter ) );
2342    
2343                    }
2344                }
2345                else
2346                {
2347                    final JExpression copyExpr;
2348                    if ( field.getPropertyInfo().ref().size() != 1 )
2349                    {
2350                        block.directStatement( "// '" + field.getPropertyInfo().getName( true ) + "' property." );
2351                        copyExpr = JExpr.invoke( this.getCopyOfPropertyMethod( field ) ).arg( o.invoke( getter ) );
2352                    }
2353                    else
2354                    {
2355                        CTypeInfo typeInfo = null;
2356    
2357                        if ( field.getPropertyInfo().getAdapter() != null
2358                             && field.getPropertyInfo().getAdapter().customType != null )
2359                        {
2360                            typeInfo = field.parent().parent().getModel().getTypeInfo(
2361                                field.getPropertyInfo().getAdapter().customType );
2362    
2363                            if ( typeInfo == null )
2364                            {
2365                                typeInfo = new CAdapterInfo( field.getPropertyInfo().getAdapter() );
2366                            }
2367                        }
2368                        else
2369                        {
2370                            typeInfo = field.getPropertyInfo().ref().iterator().next();
2371                        }
2372    
2373                        final JType javaType = typeInfo.toType( field.parent().parent(), Aspect.IMPLEMENTATION );
2374    
2375                        final JExpression source =
2376                            field.parent().parent().getModel().strategy == ImplStructureStrategy.BEAN_ONLY
2377                            ? JExpr.invoke( o, getter )
2378                            : JExpr.cast( javaType, JExpr.invoke( o, getter ) );
2379    
2380                        copyExpr = this.getCopyExpression( field, typeInfo, block, source, true );
2381                    }
2382    
2383                    if ( copyExpr == null )
2384                    {
2385                        this.log( Level.SEVERE, "cannotCopyProperty", field.getPropertyInfo().getName( true ),
2386                                  field.parent().implClass.binaryName() );
2387    
2388                    }
2389                    else
2390                    {
2391                        if ( field.getRawType().isPrimitive() )
2392                        {
2393                            block.assign( JExpr.refthis( field.getPropertyInfo().getName( false ) ), copyExpr );
2394                        }
2395                        else
2396                        {
2397                            block.assign( JExpr.refthis( field.getPropertyInfo().getName( false ) ),
2398                                          JOp.cond( JExpr.ref( o, field.getPropertyInfo().getName( false ) ).
2399                                eq( JExpr._null() ), JExpr._null(), copyExpr ) );
2400    
2401                        }
2402                    }
2403                }
2404            }
2405            else
2406            {
2407                throw new AssertionError( getMessage( "getterNotFound", field.getPropertyInfo().getName( true ),
2408                                                      field.parent().implClass.binaryName() ) );
2409    
2410            }
2411        }
2412    
2413        private String getMethodNamePart( final JType type )
2414        {
2415            String methodName = type.name();
2416            if ( type.isArray() )
2417            {
2418                methodName = methodName.replace( "[]", "s" );
2419            }
2420    
2421            methodName = methodName.replace( ".", "" );
2422            final char[] c = methodName.toCharArray();
2423            c[0] = Character.toUpperCase( c[0] );
2424            methodName = String.valueOf( c );
2425            return methodName;
2426        }
2427    
2428        private static String getMessage( final String key, final Object... args )
2429        {
2430            return MessageFormat.format(
2431                ResourceBundle.getBundle( "net/sourceforge/ccxjc/PluginImpl" ).getString( key ), args );
2432    
2433        }
2434    
2435        private Class<?> getClass( final String binaryName )
2436        {
2437            try
2438            {
2439                return Class.forName( binaryName );
2440            }
2441            catch ( final ClassNotFoundException e )
2442            {
2443                return null;
2444            }
2445        }
2446    
2447        private Collection<String> readTypes( final String fileName ) throws IOException
2448        {
2449            final Collection<String> types = new LinkedList<String>();
2450            final BufferedReader reader = new BufferedReader( new FileReader( fileName ) );
2451            String line;
2452    
2453            while ( ( line = reader.readLine() ) != null )
2454            {
2455                if ( line.indexOf( '#' ) > -1 )
2456                {
2457                    continue;
2458                }
2459    
2460                if ( line.trim().length() > 0 )
2461                {
2462                    types.add( line.trim() );
2463                }
2464            }
2465    
2466            return Collections.unmodifiableCollection( types );
2467        }
2468    
2469        private void log( final Level level, final String key, final Object... args )
2470        {
2471            final StringBuilder b = new StringBuilder( 512 ).append( "[" ).append( MESSAGE_PREFIX ).append( "] [" ).
2472                append( level.getLocalizedName() ).append( "] " ).append( getMessage( key, args ) );
2473    
2474            int logLevel = Level.WARNING.intValue();
2475            if ( this.options != null && !this.options.quiet )
2476            {
2477                if ( this.options.verbose )
2478                {
2479                    logLevel = Level.INFO.intValue();
2480                }
2481                if ( this.options.debugMode )
2482                {
2483                    logLevel = Level.ALL.intValue();
2484                }
2485            }
2486    
2487            if ( level.intValue() >= logLevel )
2488            {
2489                if ( level.intValue() <= Level.INFO.intValue() )
2490                {
2491                    System.out.println( b.toString() );
2492                }
2493                else
2494                {
2495                    System.err.println( b.toString() );
2496                }
2497            }
2498        }
2499    
2500    }
2501    
2502    class CClassInfoComparator implements Comparator<CClassInfo>
2503    {
2504    
2505        private final Outline outline;
2506    
2507        CClassInfoComparator( final Outline outline )
2508        {
2509            this.outline = outline;
2510        }
2511    
2512        public int compare( final CClassInfo o1, final CClassInfo o2 )
2513        {
2514            final JClass javaClass1 = o1.toType( this.outline, Aspect.IMPLEMENTATION );
2515            final JClass javaClass2 = o2.toType( this.outline, Aspect.IMPLEMENTATION );
2516    
2517            int ret = 0;
2518    
2519            if ( !javaClass1.binaryName().equals( javaClass2.binaryName() ) )
2520            {
2521                if ( javaClass1.isAssignableFrom( javaClass2 ) )
2522                {
2523                    ret = -1;
2524                }
2525                else if ( javaClass2.isAssignableFrom( javaClass1 ) )
2526                {
2527                    ret = 1;
2528                }
2529            }
2530    
2531            return ret;
2532        }
2533    
2534    }
2535    
2536    class CElementInfoComparator implements Comparator<CElementInfo>
2537    {
2538    
2539        private final Outline outline;
2540    
2541        private final boolean hasClass;
2542    
2543        CElementInfoComparator( final Outline outline, final boolean hasClass )
2544        {
2545            this.outline = outline;
2546            this.hasClass = hasClass;
2547        }
2548    
2549        public int compare( final CElementInfo o1, final CElementInfo o2 )
2550        {
2551            final JClass javaClass1;
2552            final JClass javaClass2;
2553    
2554            if ( this.hasClass )
2555            {
2556                javaClass1 = (JClass) o1.toType( this.outline, Aspect.IMPLEMENTATION );
2557                javaClass2 = (JClass) o2.toType( this.outline, Aspect.IMPLEMENTATION );
2558            }
2559            else
2560            {
2561                javaClass1 = (JClass) o1.getContentType().toType( this.outline, Aspect.IMPLEMENTATION );
2562                javaClass2 = (JClass) o2.getContentType().toType( this.outline, Aspect.IMPLEMENTATION );
2563            }
2564    
2565            int ret = 0;
2566    
2567            if ( !javaClass1.binaryName().equals( javaClass2.binaryName() ) )
2568            {
2569                if ( javaClass1.isAssignableFrom( javaClass2 ) )
2570                {
2571                    ret = -1;
2572                }
2573                else if ( javaClass2.isAssignableFrom( javaClass1 ) )
2574                {
2575                    ret = 1;
2576                }
2577            }
2578    
2579            return ret;
2580        }
2581    
2582    }
2583    
2584    class CTypeInfoComparator implements Comparator<CTypeInfo>
2585    {
2586    
2587        private final Outline outline;
2588    
2589        CTypeInfoComparator( final Outline outline )
2590        {
2591            this.outline = outline;
2592        }
2593    
2594        public int compare( final CTypeInfo o1, final CTypeInfo o2 )
2595        {
2596            final JType javaType1 = o1.toType( this.outline, Aspect.IMPLEMENTATION );
2597            final JType javaType2 = o2.toType( this.outline, Aspect.IMPLEMENTATION );
2598    
2599            int ret = 0;
2600    
2601            if ( !javaType1.binaryName().equals( javaType2.binaryName() ) && javaType1 instanceof JClass
2602                 && javaType2 instanceof JClass )
2603            {
2604                if ( ( (JClass) javaType1 ).isAssignableFrom( (JClass) javaType2 ) )
2605                {
2606                    ret = -1;
2607                }
2608                else if ( ( (JClass) javaType2 ).isAssignableFrom( (JClass) javaType1 ) )
2609                {
2610                    ret = 1;
2611                }
2612            }
2613    
2614            return ret;
2615        }
2616    
2617    }
2618    
2619    class CAdapterInfo implements CTypeInfo
2620    {
2621    
2622        private final CAdapter adapter;
2623    
2624        CAdapterInfo( final CAdapter adapter )
2625        {
2626            this.adapter = adapter;
2627        }
2628    
2629        public JType toType( final Outline o, final Aspect aspect )
2630        {
2631            return this.adapter.customType.toType( o, aspect );
2632        }
2633    
2634        public NType getType()
2635        {
2636            return this.adapter.customType;
2637        }
2638    
2639        public boolean canBeReferencedByIDREF()
2640        {
2641            return false;
2642        }
2643    
2644        public Locatable getUpstream()
2645        {
2646            return null;
2647        }
2648    
2649        public Location getLocation()
2650        {
2651            return null;
2652        }
2653    
2654        public CCustomizations getCustomizations()
2655        {
2656            return null;
2657        }
2658    
2659        public Locator getLocator()
2660        {
2661            return null;
2662        }
2663    
2664        public XSComponent getSchemaComponent()
2665        {
2666            return null;
2667        }
2668    
2669        public QName getTypeName()
2670        {
2671            throw new UnsupportedOperationException( "Not supported yet." );
2672        }
2673    
2674        public boolean isSimpleType()
2675        {
2676            throw new UnsupportedOperationException( "Not supported yet." );
2677        }
2678    
2679        public boolean isCollection()
2680        {
2681            return false;
2682        }
2683    
2684        public CAdapter getAdapterUse()
2685        {
2686            return this.adapter;
2687        }
2688    
2689        public CTypeInfo getInfo()
2690        {
2691            return this;
2692        }
2693    
2694        public ID idUse()
2695        {
2696            return null;
2697        }
2698    
2699        public MimeType getExpectedMimeType()
2700        {
2701            return null;
2702        }
2703    
2704        public JExpression createConstant( Outline outline, XmlString lexical )
2705        {
2706            return null;
2707        }
2708    
2709    }