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