001 /*
002 * Copyright (C) Christian Schulte, 2005-206
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without
006 * modification, are permitted provided that the following conditions
007 * are met:
008 *
009 * o Redistributions of source code must retain the above copyright
010 * notice, this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright
013 * notice, this list of conditions and the following disclaimer in
014 * the documentation and/or other materials provided with the
015 * distribution.
016 *
017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 * $JOMC: ToolsModelProvider.java 4201 2012-01-25 09:47:12Z schulte2005 $
029 *
030 */
031 package org.jomc.tools.modlet;
032
033 import java.lang.reflect.Field;
034 import java.text.MessageFormat;
035 import java.util.HashMap;
036 import java.util.List;
037 import java.util.Locale;
038 import java.util.Map;
039 import java.util.ResourceBundle;
040 import java.util.Set;
041 import java.util.logging.Level;
042 import javax.xml.bind.JAXBElement;
043 import javax.xml.namespace.QName;
044 import org.jomc.model.Dependencies;
045 import org.jomc.model.Implementation;
046 import org.jomc.model.InheritanceModel;
047 import org.jomc.model.Messages;
048 import org.jomc.model.Module;
049 import org.jomc.model.Modules;
050 import org.jomc.model.Properties;
051 import org.jomc.model.Specification;
052 import org.jomc.model.Specifications;
053 import org.jomc.model.modlet.ModelHelper;
054 import org.jomc.modlet.Model;
055 import org.jomc.modlet.ModelContext;
056 import org.jomc.modlet.ModelException;
057 import org.jomc.modlet.ModelProvider;
058 import org.jomc.tools.JomcTool;
059 import org.jomc.tools.model.ObjectFactory;
060 import org.jomc.tools.model.SourceFileType;
061 import org.jomc.tools.model.SourceFilesType;
062 import org.jomc.tools.model.SourceSectionType;
063 import org.jomc.tools.model.SourceSectionsType;
064 import static org.jomc.tools.modlet.ToolsModletConstants.*;
065
066 /**
067 * Object management and configuration tools {@code ModelProvider} implementation.
068 *
069 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
070 * @version $JOMC: ToolsModelProvider.java 4201 2012-01-25 09:47:12Z schulte2005 $
071 * @see ModelContext#findModel(java.lang.String)
072 * @since 1.2
073 */
074 public class ToolsModelProvider implements ModelProvider
075 {
076
077 /** Constant for the qualified name of {@code source-files} elements. */
078 private static final QName SOURCE_FILES_QNAME = new ObjectFactory().createSourceFiles( null ).getName();
079
080 /**
081 * Constant for the name of the model context attribute backing property {@code enabled}.
082 * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
083 * @see ModelContext#getAttribute(java.lang.String)
084 */
085 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.tools.modlet.ToolsModelProvider.enabledAttribute";
086
087 /**
088 * Constant for the name of the system property controlling property {@code defaultEnabled}.
089 * @see #isDefaultEnabled()
090 */
091 private static final String DEFAULT_ENABLED_PROPERTY_NAME =
092 "org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled";
093
094 /**
095 * Default value of the flag indicating the provider is enabled by default.
096 * @see #isDefaultEnabled()
097 */
098 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
099
100 /** Flag indicating the provider is enabled by default. */
101 private static volatile Boolean defaultEnabled;
102
103 /** Flag indicating the provider is enabled. */
104 private Boolean enabled;
105
106 /**
107 * Constant for the name of the model context attribute backing property
108 * {@code modelObjectClasspathResolutionEnabled}.
109 *
110 * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
111 * @see ModelContext#getAttribute(java.lang.String)
112 */
113 public static final String MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME =
114 "org.jomc.tools.modlet.ToolsModelProvider.modelObjectClasspathResolutionEnabledAttribute";
115
116 /**
117 * Constant for the name of the system property controlling property
118 * {@code defaultModelObjectClasspathResolutionEnabled}.
119 * @see #isDefaultModelObjectClasspathResolutionEnabled()
120 */
121 private static final String DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME =
122 "org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled";
123
124 /**
125 * Default value of the flag indicating model object class path resolution is enabled by default.
126 * @see #isDefaultModelObjectClasspathResolutionEnabled()
127 */
128 private static final Boolean DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED = Boolean.TRUE;
129
130 /** Flag indicating model object class path resolution is enabled by default. */
131 private static volatile Boolean defaultModelObjectClasspathResolutionEnabled;
132
133 /** Flag indicating model object class path resolution is enabled. */
134 private Boolean modelObjectClasspathResolutionEnabled;
135
136 /** Creates a new {@code ToolsModelProvider} instance. */
137 public ToolsModelProvider()
138 {
139 super();
140 }
141
142 /**
143 * Gets a flag indicating the provider is enabled by default.
144 * <p>The default enabled flag is controlled by system property
145 * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultEnabled} holding a value indicating the provider is
146 * enabled by default. If that property is not set, the {@code true} default is returned.</p>
147 *
148 * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
149 * default.
150 *
151 * @see #setDefaultEnabled(java.lang.Boolean)
152 */
153 public static boolean isDefaultEnabled()
154 {
155 if ( defaultEnabled == null )
156 {
157 defaultEnabled = Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
158 Boolean.toString( DEFAULT_ENABLED ) ) );
159
160 }
161
162 return defaultEnabled;
163 }
164
165 /**
166 * Sets the flag indicating the provider is enabled by default.
167 *
168 * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
169 *
170 * @see #isDefaultEnabled()
171 */
172 public static void setDefaultEnabled( final Boolean value )
173 {
174 defaultEnabled = value;
175 }
176
177 /**
178 * Gets a flag indicating the provider is enabled.
179 *
180 * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
181 *
182 * @see #isDefaultEnabled()
183 * @see #setEnabled(java.lang.Boolean)
184 */
185 public final boolean isEnabled()
186 {
187 if ( this.enabled == null )
188 {
189 this.enabled = isDefaultEnabled();
190 }
191
192 return this.enabled;
193 }
194
195 /**
196 * Sets the flag indicating the provider is enabled.
197 *
198 * @param value The new value of the flag indicating the provider is enabled or {@code null}.
199 *
200 * @see #isEnabled()
201 */
202 public final void setEnabled( final Boolean value )
203 {
204 this.enabled = value;
205 }
206
207 /**
208 * Gets a flag indicating model object class path resolution is enabled by default.
209 * <p>The model object class path resolution default enabled flag is controlled by system property
210 * {@code org.jomc.tools.modlet.ToolsModelProvider.defaultModelObjectClasspathResolutionEnabled} holding a value
211 * indicating model object class path resolution is enabled by default. If that property is not set, the
212 * {@code true} default is returned.</p>
213 *
214 * @return {@code true}, if model object class path resolution is enabled by default; {@code false}, if model object
215 * class path resolution is disabled by default.
216 *
217 * @see #setDefaultModelObjectClasspathResolutionEnabled(java.lang.Boolean)
218 */
219 public static boolean isDefaultModelObjectClasspathResolutionEnabled()
220 {
221 if ( defaultModelObjectClasspathResolutionEnabled == null )
222 {
223 defaultModelObjectClasspathResolutionEnabled = Boolean.valueOf( System.getProperty(
224 DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_PROPERTY_NAME,
225 Boolean.toString( DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED ) ) );
226
227 }
228
229 return defaultModelObjectClasspathResolutionEnabled;
230 }
231
232 /**
233 * Sets the flag indicating model object class path resolution is enabled by default.
234 *
235 * @param value The new value of the flag indicating model object class path resolution is enabled by default or
236 * {@code null}.
237 *
238 * @see #isDefaultModelObjectClasspathResolutionEnabled()
239 */
240 public static void setDefaultModelObjectClasspathResolutionEnabled( final Boolean value )
241 {
242 defaultModelObjectClasspathResolutionEnabled = value;
243 }
244
245 /**
246 * Gets a flag indicating model object class path resolution is enabled.
247 *
248 * @return {@code true}, if model object class path resolution is enabled; {@code false}, if model object class path
249 * resolution is disabled.
250 *
251 * @see #isDefaultModelObjectClasspathResolutionEnabled()
252 * @see #setModelObjectClasspathResolutionEnabled(java.lang.Boolean)
253 */
254 public final boolean isModelObjectClasspathResolutionEnabled()
255 {
256 if ( this.modelObjectClasspathResolutionEnabled == null )
257 {
258 this.modelObjectClasspathResolutionEnabled = isDefaultModelObjectClasspathResolutionEnabled();
259 }
260
261 return this.modelObjectClasspathResolutionEnabled;
262 }
263
264 /**
265 * Sets the flag indicating model object class path resolution is is enabled.
266 *
267 * @param value The new value of the flag indicating model object class path resolution is enabled or {@code null}.
268 *
269 * @see #isModelObjectClasspathResolutionEnabled()
270 */
271 public final void setModelObjectClasspathResolutionEnabled( final Boolean value )
272 {
273 this.modelObjectClasspathResolutionEnabled = value;
274 }
275
276 /**
277 * {@inheritDoc}
278 *
279 * @see #isEnabled()
280 * @see #isModelObjectClasspathResolutionEnabled()
281 * @see #ENABLED_ATTRIBUTE_NAME
282 * @see #MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME
283 */
284 public Model findModel( final ModelContext context, final Model model ) throws ModelException
285 {
286 if ( context == null )
287 {
288 throw new NullPointerException( "context" );
289 }
290 if ( model == null )
291 {
292 throw new NullPointerException( "model" );
293 }
294
295 Model provided = null;
296
297 boolean contextEnabled = this.isEnabled();
298 if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
299 {
300 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
301 }
302
303 boolean contextModelObjectClasspathResolutionEnabled = this.isModelObjectClasspathResolutionEnabled();
304 if ( contextModelObjectClasspathResolutionEnabled == DEFAULT_MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED
305 && context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
306 {
307 contextModelObjectClasspathResolutionEnabled =
308 (Boolean) context.getAttribute( MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME );
309
310 }
311
312 if ( contextEnabled )
313 {
314 provided = model.clone();
315 final Modules modules = ModelHelper.getModules( provided );
316
317 if ( modules != null )
318 {
319 Module classpathModule = null;
320 if ( contextModelObjectClasspathResolutionEnabled )
321 {
322 classpathModule = modules.getClasspathModule( Modules.getDefaultClasspathModuleName(),
323 context.getClassLoader() );
324
325 if ( classpathModule != null
326 && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
327 {
328 modules.getModule().add( classpathModule );
329 }
330 else
331 {
332 classpathModule = null;
333 }
334 }
335
336 final JomcTool tool = new JomcTool();
337 tool.setModel( provided );
338
339 if ( modules.getSpecifications() != null )
340 {
341 for ( int i = 0, s0 = modules.getSpecifications().getSpecification().size(); i < s0; i++ )
342 {
343 final Specification specification = modules.getSpecifications().getSpecification().get( i );
344 final SourceFileType sourceFileType = specification.getAnyObject( SourceFileType.class );
345 final SourceFilesType sourceFilesType = specification.getAnyObject( SourceFilesType.class );
346
347 if ( sourceFileType == null && sourceFilesType == null && specification.isClassDeclaration() )
348 {
349 specification.getAny().add( new ObjectFactory().createSourceFiles(
350 this.getDefaultSourceFilesType( tool, specification ) ) );
351
352 }
353 }
354 }
355
356 if ( modules.getImplementations() != null )
357 {
358 final Map<Implementation, SourceFilesType> userSourceFiles =
359 new HashMap<Implementation, SourceFilesType>();
360
361 InheritanceModel imodel = new InheritanceModel( modules );
362
363 for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
364 {
365 final Implementation implementation = modules.getImplementations().getImplementation().get( i );
366 final SourceFileType sourceFileType = implementation.getAnyObject( SourceFileType.class );
367 final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
368
369 if ( sourceFileType == null )
370 {
371 if ( sourceFilesType != null )
372 {
373 userSourceFiles.put( implementation, sourceFilesType );
374 }
375 else if ( implementation.isClassDeclaration() )
376 {
377 final SourceFilesType defaultSourceFiles =
378 this.getDefaultSourceFilesType( tool, implementation );
379
380 boolean finalAncestor = false;
381
382 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
383 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
384
385 for ( final InheritanceModel.Node<JAXBElement<?>> sourceFilesNode : sourceFilesNodes )
386 {
387 if ( sourceFilesNode.getModelObject().getValue() instanceof SourceFilesType )
388 {
389 final SourceFilesType ancestorSourceFiles =
390 (SourceFilesType) sourceFilesNode.getModelObject().getValue();
391
392 this.overwriteSourceFiles( defaultSourceFiles, ancestorSourceFiles, false );
393
394 if ( ancestorSourceFiles.isFinal() )
395 {
396 finalAncestor = true;
397 }
398 }
399 }
400
401 if ( !finalAncestor )
402 {
403 implementation.getAny().add(
404 new ObjectFactory().createSourceFiles( defaultSourceFiles ) );
405
406 }
407 }
408 }
409 }
410
411 for ( final Map.Entry<Implementation, SourceFilesType> e : userSourceFiles.entrySet() )
412 {
413 this.overwriteSourceFiles( e.getValue(), this.getDefaultSourceFilesType( tool, e.getKey() ),
414 true );
415
416 }
417
418 imodel = new InheritanceModel( modules );
419
420 for ( int i = 0, s0 = modules.getImplementations().getImplementation().size(); i < s0; i++ )
421 {
422 final Implementation implementation = modules.getImplementations().getImplementation().get( i );
423 final SourceFilesType sourceFilesType = implementation.getAnyObject( SourceFilesType.class );
424
425 if ( sourceFilesType != null && !userSourceFiles.containsKey( implementation ) )
426 {
427 boolean override = false;
428
429 final Set<InheritanceModel.Node<JAXBElement<?>>> sourceFilesNodes =
430 imodel.getJaxbElementNodes( implementation.getIdentifier(), SOURCE_FILES_QNAME );
431
432 for ( final InheritanceModel.Node<JAXBElement<?>> e : sourceFilesNodes )
433 {
434 if ( !e.getOverriddenNodes().isEmpty() )
435 {
436 override = true;
437 break;
438 }
439 }
440
441 if ( override )
442 {
443 sourceFilesType.setOverride( override );
444 }
445 }
446 }
447 }
448
449 if ( classpathModule != null )
450 {
451 modules.getModule().remove( classpathModule );
452 }
453 }
454 }
455 else if ( context.isLoggable( Level.FINER ) )
456 {
457 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
458 model.getIdentifier() ), null );
459
460 }
461
462 return provided;
463 }
464
465 /**
466 * Creates a new default source files model for a given specification.
467 *
468 * @param tool The tool to use for generating type names.
469 * @param specification The specification to create a new default source files model for.
470 *
471 * @return A new default source files model for {@code specification}.
472 *
473 * @throws NullPointerExeption if {@code tool} or {@code specification} is {@code null}.
474 */
475 private SourceFilesType getDefaultSourceFilesType( final JomcTool tool, final Specification specification )
476 {
477 if ( tool == null )
478 {
479 throw new NullPointerException( "tool" );
480 }
481 if ( specification == null )
482 {
483 throw new NullPointerException( "specification" );
484 }
485
486 final SourceFilesType sourceFilesType = new SourceFilesType();
487 final SourceFileType sourceFileType = new SourceFileType();
488 sourceFilesType.getSourceFile().add( sourceFileType );
489
490 sourceFileType.setIdentifier( "Default" );
491
492 if ( specification.getClazz() != null )
493 {
494 sourceFileType.setLocation( new StringBuilder( specification.getClazz().length() + 5 ).append(
495 specification.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
496
497 }
498
499 sourceFileType.setTemplate( SPECIFICATION_TEMPLATE );
500 sourceFileType.setHeadComment( "//" );
501 sourceFileType.setSourceSections( new SourceSectionsType() );
502
503 SourceSectionType s = new SourceSectionType();
504 s.setName( LICENSE_SECTION_NAME );
505 s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
506 s.setOptional( true );
507 sourceFileType.getSourceSections().getSourceSection().add( s );
508
509 s = new SourceSectionType();
510 s.setName( ANNOTATIONS_SECTION_NAME );
511 s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
512 sourceFileType.getSourceSections().getSourceSection().add( s );
513
514 s = new SourceSectionType();
515 s.setName( DOCUMENTATION_SECTION_NAME );
516 s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
517 s.setOptional( true );
518 sourceFileType.getSourceSections().getSourceSection().add( s );
519
520 final String javaTypeName = tool.getJavaTypeName( specification, false );
521 if ( javaTypeName != null )
522 {
523 s = new SourceSectionType();
524 s.setName( javaTypeName );
525 s.setIndentationLevel( 1 );
526 s.setEditable( true );
527 sourceFileType.getSourceSections().getSourceSection().add( s );
528 }
529
530 return sourceFilesType;
531 }
532
533 /**
534 * Creates a new default source files model for a given implementation.
535 *
536 * @param tool The tool to use for generating type names.
537 * @param implementation The implementation to create a new default source files model for.
538 *
539 * @return A new default source files model for {@code implementation}.
540 *
541 * @throws NullPointerExeption if {@code tool} or {@code implementation} is {@code null}.
542 */
543 private SourceFilesType getDefaultSourceFilesType( final JomcTool tool, final Implementation implementation )
544 {
545 if ( tool == null )
546 {
547 throw new NullPointerException( "tool" );
548 }
549 if ( implementation == null )
550 {
551 throw new NullPointerException( "implementation" );
552 }
553
554 final SourceFilesType sourceFilesType = new SourceFilesType();
555 final SourceFileType sourceFileType = new SourceFileType();
556 sourceFilesType.getSourceFile().add( sourceFileType );
557
558 final Modules modules = ModelHelper.getModules( tool.getModel() );
559 Specifications specifications = null;
560 Dependencies dependencies = null;
561 Messages messages = null;
562 Properties properties = null;
563
564 if ( modules != null )
565 {
566 specifications = modules.getSpecifications( implementation.getIdentifier() );
567 dependencies = modules.getDependencies( implementation.getIdentifier() );
568 messages = modules.getMessages( implementation.getIdentifier() );
569 properties = modules.getProperties( implementation.getIdentifier() );
570 }
571
572 sourceFileType.setIdentifier( "Default" );
573
574 if ( implementation.getClazz() != null )
575 {
576 sourceFileType.setLocation( new StringBuilder( implementation.getClazz().length() + 5 ).append(
577 implementation.getClazz().replace( '.', '/' ) ).append( ".java" ).toString() );
578
579 }
580
581 sourceFileType.setTemplate( IMPLEMENTATION_TEMPLATE );
582 sourceFileType.setHeadComment( "//" );
583 sourceFileType.setSourceSections( new SourceSectionsType() );
584
585 SourceSectionType s = new SourceSectionType();
586 s.setName( LICENSE_SECTION_NAME );
587 s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
588 s.setOptional( true );
589 sourceFileType.getSourceSections().getSourceSection().add( s );
590
591 s = new SourceSectionType();
592 s.setName( ANNOTATIONS_SECTION_NAME );
593 s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
594 sourceFileType.getSourceSections().getSourceSection().add( s );
595
596 s = new SourceSectionType();
597 s.setName( DOCUMENTATION_SECTION_NAME );
598 s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
599 s.setOptional( true );
600 sourceFileType.getSourceSections().getSourceSection().add( s );
601
602 final List<String> implementedJavaTypeNames = tool.getImplementedJavaTypeNames( implementation, false );
603 for ( int i = 0, s0 = implementedJavaTypeNames.size(); i < s0; i++ )
604 {
605 s = new SourceSectionType();
606 s.setName( implementedJavaTypeNames.get( i ) );
607 s.setIndentationLevel( 1 );
608 s.setEditable( true );
609 sourceFileType.getSourceSections().getSourceSection().add( s );
610 }
611
612 final String javaTypeName = tool.getJavaTypeName( implementation, false );
613 if ( javaTypeName != null && !implementedJavaTypeNames.contains( javaTypeName ) )
614 {
615 s = new SourceSectionType();
616 s.setName( javaTypeName );
617 s.setIndentationLevel( 1 );
618 s.setEditable( true );
619 sourceFileType.getSourceSections().getSourceSection().add( s );
620 }
621
622 s = new SourceSectionType();
623 s.setName( CONSTRUCTORS_SECTION_NAME );
624 s.setIndentationLevel( 1 );
625 s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
626 s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
627 s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
628 && specifications.getReference().isEmpty() ) );
629
630 s.setSourceSections( new SourceSectionsType() );
631 sourceFileType.getSourceSections().getSourceSection().add( s );
632
633 final SourceSectionType defaultCtor = new SourceSectionType();
634 defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
635 defaultCtor.setIndentationLevel( 2 );
636 defaultCtor.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
637 defaultCtor.setEditable( true );
638 s.getSourceSections().getSourceSection().add( defaultCtor );
639
640 s = new SourceSectionType();
641 s.setName( DEPENDENCIES_SECTION_NAME );
642 s.setIndentationLevel( 1 );
643 s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
644 s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
645 sourceFileType.getSourceSections().getSourceSection().add( s );
646
647 s = new SourceSectionType();
648 s.setName( PROPERTIES_SECTION_NAME );
649 s.setIndentationLevel( 1 );
650 s.setHeadTemplate( PROPERTIES_TEMPLATE );
651 s.setOptional( properties == null || properties.getProperty().isEmpty() );
652 sourceFileType.getSourceSections().getSourceSection().add( s );
653
654 s = new SourceSectionType();
655 s.setName( MESSAGES_SECTION_NAME );
656 s.setIndentationLevel( 1 );
657 s.setHeadTemplate( MESSAGES_TEMPLATE );
658 s.setOptional( messages == null || messages.getMessage().isEmpty() );
659 sourceFileType.getSourceSections().getSourceSection().add( s );
660
661 return sourceFilesType;
662 }
663
664 /**
665 * Overwrites a list of source code files with another list of source code files.
666 *
667 * @param targetSourceFiles The list to overwrite.
668 * @param sourceSourceFiles The list to overwrite with.
669 * @param preserveExisting {@code true}, to preserve existing attributes of source code files and sections;
670 * {@code false}, to overwrite existing attributes of source code files and sections.
671 *
672 * @throws NullPointerException if {@code targetSourceFiles} or {@code sourceSourceFiles} is {@code null}.
673 */
674 private void overwriteSourceFiles( final SourceFilesType targetSourceFiles, final SourceFilesType sourceSourceFiles,
675 final boolean preserveExisting )
676 {
677 if ( targetSourceFiles == null )
678 {
679 throw new NullPointerException( "targetSourceFiles" );
680 }
681 if ( sourceSourceFiles == null )
682 {
683 throw new NullPointerException( "sourceSourceFiles" );
684 }
685
686 try
687 {
688 for ( final SourceFileType s : sourceSourceFiles.getSourceFile() )
689 {
690 final SourceFileType targetSourceFile = targetSourceFiles.getSourceFile( s.getIdentifier() );
691
692 if ( targetSourceFile != null )
693 {
694 this.overwriteSourceFile( targetSourceFile, s, preserveExisting );
695 }
696 }
697 }
698 catch ( final NoSuchFieldException e )
699 {
700 throw new AssertionError( e );
701 }
702 }
703
704 /**
705 * Overwrites a source code file with another source code file.
706 *
707 * @param targetSourceFile The source code file to overwrite.
708 * @param sourceSourceFile The source code file to overwrite with.
709 * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file and sections;
710 * {@code false}, to overwrite existing attributes of the given source code file and sections.
711 *
712 * @throws NullPointerException if {@code targetSourceFile} or {@code sourceSourceFile} is {@code null}.
713 */
714 private void overwriteSourceFile( final SourceFileType targetSourceFile, final SourceFileType sourceSourceFile,
715 final boolean preserveExisting )
716 throws NoSuchFieldException
717 {
718 if ( targetSourceFile == null )
719 {
720 throw new NullPointerException( "targetSourceFile" );
721 }
722 if ( sourceSourceFile == null )
723 {
724 throw new NullPointerException( "sourceSourceFile" );
725 }
726
727 if ( !preserveExisting )
728 {
729 targetSourceFile.setIdentifier( sourceSourceFile.getIdentifier() );
730 targetSourceFile.setLocation( sourceSourceFile.getLocation() );
731 targetSourceFile.setTemplate( sourceSourceFile.getTemplate() );
732 targetSourceFile.setHeadComment( sourceSourceFile.getHeadComment() );
733 targetSourceFile.setTailComment( sourceSourceFile.getTailComment() );
734
735 if ( isFieldSet( sourceSourceFile, "_final" ) )
736 {
737 targetSourceFile.setFinal( sourceSourceFile.isFinal() );
738 }
739 if ( isFieldSet( sourceSourceFile, "modelVersion" ) )
740 {
741 targetSourceFile.setModelVersion( sourceSourceFile.getModelVersion() );
742 }
743 if ( isFieldSet( sourceSourceFile, "override" ) )
744 {
745 targetSourceFile.setOverride( sourceSourceFile.isOverride() );
746 }
747 }
748
749 if ( sourceSourceFile.getSourceSections() != null )
750 {
751 if ( targetSourceFile.getSourceSections() == null )
752 {
753 targetSourceFile.setSourceSections( new SourceSectionsType() );
754 }
755
756 this.overwriteSourceSections( targetSourceFile.getSourceSections(), sourceSourceFile.getSourceSections(),
757 preserveExisting );
758
759 }
760 }
761
762 /**
763 * Overwrites source code file sections with other source code file sections.
764 *
765 * @param targetSourceSections The source code file sections to overwrite.
766 * @param sourceSourceSections The source code file sections to overwrite with.
767 * @param preserveExisting {@code true}, to preserve existing attributes of the given source code file sections;
768 * {@code false}, to overwrite existing attributes of the given source code file sections.
769 *
770 * @throws NullPointerException if {@code targetSourceSections} or {@code sourceSourceSections} is {@code null}.
771 */
772 private void overwriteSourceSections( final SourceSectionsType targetSourceSections,
773 final SourceSectionsType sourceSourceSections,
774 final boolean preserveExisting ) throws NoSuchFieldException
775 {
776 if ( targetSourceSections == null )
777 {
778 throw new NullPointerException( "targetSourceSections" );
779 }
780 if ( sourceSourceSections == null )
781 {
782 throw new NullPointerException( "sourceSourceSections" );
783 }
784
785 for ( final SourceSectionType sourceSection : sourceSourceSections.getSourceSection() )
786 {
787 SourceSectionType targetSection = null;
788
789 for ( final SourceSectionType t : targetSourceSections.getSourceSection() )
790 {
791 if ( sourceSection.getName().equals( t.getName() ) )
792 {
793 targetSection = t;
794 break;
795 }
796 }
797
798 if ( targetSection != null )
799 {
800 if ( !preserveExisting )
801 {
802 targetSection.setName( sourceSection.getName() );
803 targetSection.setHeadTemplate( sourceSection.getHeadTemplate() );
804 targetSection.setTailTemplate( sourceSection.getTailTemplate() );
805
806 if ( isFieldSet( sourceSection, "editable" ) )
807 {
808 targetSection.setEditable( sourceSection.isEditable() );
809 }
810 if ( isFieldSet( sourceSection, "indentationLevel" ) )
811 {
812 targetSection.setIndentationLevel( sourceSection.getIndentationLevel() );
813 }
814 if ( isFieldSet( sourceSection, "modelVersion" ) )
815 {
816 targetSection.setModelVersion( sourceSection.getModelVersion() );
817 }
818 if ( isFieldSet( sourceSection, "optional" ) )
819 {
820 targetSection.setOptional( sourceSection.isOptional() );
821 }
822 }
823 }
824 else
825 {
826 targetSection = sourceSection.clone();
827 targetSourceSections.getSourceSection().add( targetSection );
828 }
829
830 if ( sourceSection.getSourceSections() != null )
831 {
832 if ( targetSection.getSourceSections() == null )
833 {
834 targetSection.setSourceSections( new SourceSectionsType() );
835 }
836
837 this.overwriteSourceSections( targetSection.getSourceSections(), sourceSection.getSourceSections(),
838 preserveExisting );
839 }
840 }
841 }
842
843 private static boolean isFieldSet( final Object object, final String fieldName ) throws NoSuchFieldException
844 {
845 final Field field = getField( object.getClass(), fieldName );
846
847 if ( field == null )
848 {
849 throw new NoSuchFieldException( fieldName );
850 }
851
852 final boolean accessible = field.isAccessible();
853
854 try
855 {
856 field.setAccessible( true );
857 return field.get( object ) != null;
858 }
859 catch ( final IllegalAccessException e )
860 {
861 throw new AssertionError( e );
862 }
863 finally
864 {
865 field.setAccessible( accessible );
866 }
867 }
868
869 private static Field getField( final Class<?> clazz, final String name )
870 {
871 if ( clazz != null )
872 {
873 try
874 {
875 return clazz.getDeclaredField( name );
876 }
877 catch ( final NoSuchFieldException e )
878 {
879 return getField( clazz.getSuperclass(), name );
880 }
881 }
882
883 return null;
884 }
885
886 private static String getMessage( final String key, final Object... args )
887 {
888 return MessageFormat.format( ResourceBundle.getBundle(
889 ToolsModelProvider.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
890
891 }
892
893 }