View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte <cs@schulte.it>, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: MergeModletsTask.java 5043 2015-05-27 07:03:39Z schulte $
29   *
30   */
31  package org.jomc.ant;
32  
33  import java.io.ByteArrayOutputStream;
34  import java.io.File;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.OutputStreamWriter;
38  import java.net.SocketTimeoutException;
39  import java.net.URISyntaxException;
40  import java.net.URL;
41  import java.net.URLConnection;
42  import java.util.ArrayList;
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.LinkedList;
46  import java.util.List;
47  import java.util.Set;
48  import java.util.logging.Level;
49  import javax.xml.bind.JAXBElement;
50  import javax.xml.bind.JAXBException;
51  import javax.xml.bind.Marshaller;
52  import javax.xml.bind.Unmarshaller;
53  import javax.xml.bind.util.JAXBResult;
54  import javax.xml.bind.util.JAXBSource;
55  import javax.xml.transform.Source;
56  import javax.xml.transform.Transformer;
57  import javax.xml.transform.TransformerConfigurationException;
58  import javax.xml.transform.TransformerException;
59  import javax.xml.transform.stream.StreamSource;
60  import org.apache.tools.ant.BuildException;
61  import org.apache.tools.ant.Project;
62  import org.jomc.ant.types.ModletResourceType;
63  import org.jomc.ant.types.NameType;
64  import org.jomc.ant.types.ResourceType;
65  import org.jomc.ant.types.TransformerResourceType;
66  import org.jomc.modlet.DefaultModletProvider;
67  import org.jomc.modlet.ModelContext;
68  import org.jomc.modlet.ModelException;
69  import org.jomc.modlet.ModelValidationReport;
70  import org.jomc.modlet.Modlet;
71  import org.jomc.modlet.ModletObject;
72  import org.jomc.modlet.Modlets;
73  import org.jomc.modlet.ObjectFactory;
74  
75  /**
76   * Task for merging modlet resources.
77   *
78   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
79   * @version $JOMC: MergeModletsTask.java 5043 2015-05-27 07:03:39Z schulte $
80   */
81  public final class MergeModletsTask extends JomcTask
82  {
83  
84      /**
85       * The encoding of the modlet resource.
86       */
87      private String modletEncoding;
88  
89      /**
90       * File to write the merged modlet to.
91       */
92      private File modletFile;
93  
94      /**
95       * The name of the merged modlet.
96       */
97      private String modletName;
98  
99      /**
100      * The version of the merged modlet.
101      */
102     private String modletVersion;
103 
104     /**
105      * The vendor of the merged modlet.
106      */
107     private String modletVendor;
108 
109     /**
110      * Resources to merge.
111      */
112     private Set<ModletResourceType> modletResources;
113 
114     /**
115      * Included modlets.
116      */
117     private Set<NameType> modletIncludes;
118 
119     /**
120      * Excluded modlets.
121      */
122     private Set<NameType> modletExcludes;
123 
124     /**
125      * XSLT documents to use for transforming modlet objects.
126      */
127     private List<TransformerResourceType> modletObjectStylesheetResources;
128 
129     /**
130      * Creates a new {@code MergeModletsTask} instance.
131      */
132     public MergeModletsTask()
133     {
134         super();
135     }
136 
137     /**
138      * Gets the file to write the merged modlet to.
139      *
140      * @return The file to write the merged modlet to or {@code null}.
141      *
142      * @see #setModletFile(java.io.File)
143      */
144     public File getModletFile()
145     {
146         return this.modletFile;
147     }
148 
149     /**
150      * Sets the file to write the merged modlet to.
151      *
152      * @param value The new file to write the merged modlet to or {@code null}.
153      *
154      * @see #getModletFile()
155      */
156     public void setModletFile( final File value )
157     {
158         this.modletFile = value;
159     }
160 
161     /**
162      * Gets the encoding of the modlet resource.
163      *
164      * @return The encoding of the modlet resource.
165      *
166      * @see #setModletEncoding(java.lang.String)
167      */
168     public String getModletEncoding()
169     {
170         if ( this.modletEncoding == null )
171         {
172             this.modletEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
173         }
174 
175         return this.modletEncoding;
176     }
177 
178     /**
179      * Sets the encoding of the modlet resource.
180      *
181      * @param value The new encoding of the modlet resource or {@code null}.
182      *
183      * @see #getModletEncoding()
184      */
185     public void setModletEncoding( final String value )
186     {
187         this.modletEncoding = value;
188     }
189 
190     /**
191      * Gets the name of the merged modlet.
192      *
193      * @return The name of the merged modlet or {@code null}.
194      *
195      * @see #setModletName(java.lang.String)
196      */
197     public String getModletName()
198     {
199         return this.modletName;
200     }
201 
202     /**
203      * Sets the name of the merged modlet.
204      *
205      * @param value The new name of the merged modlet or {@code null}.
206      *
207      * @see #getModletName()
208      */
209     public void setModletName( final String value )
210     {
211         this.modletName = value;
212     }
213 
214     /**
215      * Gets the version of the merged modlet.
216      *
217      * @return The version of the merged modlet or {@code null}.
218      *
219      * @see #setModletVersion(java.lang.String)
220      */
221     public String getModletVersion()
222     {
223         return this.modletVersion;
224     }
225 
226     /**
227      * Sets the version of the merged modlet.
228      *
229      * @param value The new version of the merged modlet or {@code null}.
230      *
231      * @see #getModletVersion()
232      */
233     public void setModletVersion( final String value )
234     {
235         this.modletVersion = value;
236     }
237 
238     /**
239      * Gets the vendor of the merged modlet.
240      *
241      * @return The vendor of the merge modlet or {@code null}.
242      *
243      * @see #setModletVendor(java.lang.String)
244      */
245     public String getModletVendor()
246     {
247         return this.modletVendor;
248     }
249 
250     /**
251      * Sets the vendor of the merged modlet.
252      *
253      * @param value The new vendor of the merged modlet or {@code null}.
254      *
255      * @see #getModletVendor()
256      */
257     public void setModletVendor( final String value )
258     {
259         this.modletVendor = value;
260     }
261 
262     /**
263      * Gets a set of resource names to merge.
264      * <p>
265      * This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
266      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
267      * modlet resources property.
268      * </p>
269      *
270      * @return A set of names of resources to merge.
271      *
272      * @see #createModletResource()
273      */
274     public Set<ModletResourceType> getModletResources()
275     {
276         if ( this.modletResources == null )
277         {
278             this.modletResources = new HashSet<ModletResourceType>();
279         }
280 
281         return this.modletResources;
282     }
283 
284     /**
285      * Creates a new {@code modletResource} element instance.
286      *
287      * @return A new {@code modletResource} element instance.
288      *
289      * @see #getModletResources()
290      */
291     public ModletResourceType createModletResource()
292     {
293         final ModletResourceType modletResource = new ModletResourceType();
294         this.getModletResources().add( modletResource );
295         return modletResource;
296     }
297 
298     /**
299      * Gets a set of modlet names to include.
300      * <p>
301      * This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
302      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
303      * modlet includes property.
304      * </p>
305      *
306      * @return A set of modlet names to include.
307      *
308      * @see #createModletInclude()
309      */
310     public Set<NameType> getModletIncludes()
311     {
312         if ( this.modletIncludes == null )
313         {
314             this.modletIncludes = new HashSet<NameType>();
315         }
316 
317         return this.modletIncludes;
318     }
319 
320     /**
321      * Creates a new {@code modletInclude} element instance.
322      *
323      * @return A new {@code modletInclude} element instance.
324      *
325      * @see #getModletIncludes()
326      */
327     public NameType createModletInclude()
328     {
329         final NameType modletInclude = new NameType();
330         this.getModletIncludes().add( modletInclude );
331         return modletInclude;
332     }
333 
334     /**
335      * Gets a set of modlet names to exclude.
336      * <p>
337      * This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
338      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
339      * modlet excludes property.
340      * </p>
341      *
342      * @return A set of modlet names to exclude.
343      *
344      * @see #createModletExclude()
345      */
346     public Set<NameType> getModletExcludes()
347     {
348         if ( this.modletExcludes == null )
349         {
350             this.modletExcludes = new HashSet<NameType>();
351         }
352 
353         return this.modletExcludes;
354     }
355 
356     /**
357      * Creates a new {@code modletExclude} element instance.
358      *
359      * @return A new {@code modletExclude} element instance.
360      *
361      * @see #getModletExcludes()
362      */
363     public NameType createModletExclude()
364     {
365         final NameType modletExclude = new NameType();
366         this.getModletExcludes().add( modletExclude );
367         return modletExclude;
368     }
369 
370     /**
371      * Gets the XSLT documents to use for transforming modlet objects.
372      * <p>
373      * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
374      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
375      * modlet object stylesheet resources property.
376      * </p>
377      *
378      * @return The XSLT documents to use for transforming modlet objects.
379      *
380      * @see #createModletObjectStylesheetResource()
381      */
382     public List<TransformerResourceType> getModletObjectStylesheetResources()
383     {
384         if ( this.modletObjectStylesheetResources == null )
385         {
386             this.modletObjectStylesheetResources = new LinkedList<TransformerResourceType>();
387         }
388 
389         return this.modletObjectStylesheetResources;
390     }
391 
392     /**
393      * Creates a new {@code modletObjectStylesheetResource} element instance.
394      *
395      * @return A new {@code modletObjectStylesheetResource} element instance.
396      *
397      * @see #getModletObjectStylesheetResources()
398      */
399     public TransformerResourceType createModletObjectStylesheetResource()
400     {
401         final TransformerResourceType modletObjectStylesheetResource = new TransformerResourceType();
402         this.getModletObjectStylesheetResources().add( modletObjectStylesheetResource );
403         return modletObjectStylesheetResource;
404     }
405 
406     /**
407      * {@inheritDoc}
408      */
409     @Override
410     public void preExecuteTask() throws BuildException
411     {
412         super.preExecuteTask();
413 
414         this.assertNotNull( "modletFile", this.getModletFile() );
415         this.assertNotNull( "modletName", this.getModletName() );
416         this.assertNamesNotNull( this.getModletExcludes() );
417         this.assertNamesNotNull( this.getModletIncludes() );
418         this.assertLocationsNotNull( this.getModletResources() );
419         this.assertLocationsNotNull( this.getModletObjectStylesheetResources() );
420     }
421 
422     /**
423      * Merges modlet resources.
424      *
425      * @throws BuildException if merging modlet resources fails.
426      */
427     @Override
428     public void executeTask() throws BuildException
429     {
430         ProjectClassLoader classLoader = null;
431         boolean suppressExceptionOnClose = true;
432 
433         try
434         {
435             this.log( Messages.getMessage( "mergingModlets", this.getModel() ) );
436 
437             classLoader = this.newProjectClassLoader();
438             final Modlets modlets = new Modlets();
439             final Set<ResourceType> resources = new HashSet<ResourceType>( this.getModletResources() );
440             final ModelContext context = this.newModelContext( classLoader );
441             final Marshaller marshaller = context.createMarshaller( ModletObject.MODEL_PUBLIC_ID );
442             final Unmarshaller unmarshaller = context.createUnmarshaller( ModletObject.MODEL_PUBLIC_ID );
443 
444             if ( this.isModletResourceValidationEnabled() )
445             {
446                 unmarshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
447             }
448 
449             if ( resources.isEmpty() )
450             {
451                 final ResourceType defaultResource = new ResourceType();
452                 defaultResource.setLocation( DefaultModletProvider.getDefaultModletLocation() );
453                 defaultResource.setOptional( true );
454                 resources.add( defaultResource );
455             }
456 
457             for ( final ResourceType resource : resources )
458             {
459                 final URL[] urls = this.getResources( context, resource.getLocation() );
460 
461                 if ( urls.length == 0 )
462                 {
463                     if ( resource.isOptional() )
464                     {
465                         this.logMessage( Level.WARNING, Messages.getMessage( "modletResourceNotFound",
466                                                                              resource.getLocation() ) );
467 
468                     }
469                     else
470                     {
471                         throw new BuildException( Messages.getMessage( "modletResourceNotFound",
472                                                                        resource.getLocation() ) );
473 
474                     }
475                 }
476 
477                 for ( int i = urls.length - 1; i >= 0; i-- )
478                 {
479                     InputStream in = null;
480                     suppressExceptionOnClose = true;
481 
482                     try
483                     {
484                         this.logMessage( Level.FINEST, Messages.getMessage( "reading", urls[i].toExternalForm() ) );
485 
486                         final URLConnection con = urls[i].openConnection();
487                         con.setConnectTimeout( resource.getConnectTimeout() );
488                         con.setReadTimeout( resource.getReadTimeout() );
489                         con.connect();
490                         in = con.getInputStream();
491 
492                         final Source source = new StreamSource( in, urls[i].toURI().toASCIIString() );
493                         Object o = unmarshaller.unmarshal( source );
494                         if ( o instanceof JAXBElement<?> )
495                         {
496                             o = ( (JAXBElement<?>) o ).getValue();
497                         }
498 
499                         if ( o instanceof Modlet )
500                         {
501                             modlets.getModlet().add( (Modlet) o );
502                         }
503                         else if ( o instanceof Modlets )
504                         {
505                             modlets.getModlet().addAll( ( (Modlets) o ).getModlet() );
506                         }
507                         else
508                         {
509                             this.logMessage( Level.WARNING, Messages.getMessage( "unsupportedModletResource",
510                                                                                  urls[i].toExternalForm() ) );
511 
512                         }
513 
514                         suppressExceptionOnClose = false;
515                     }
516                     catch ( final SocketTimeoutException e )
517                     {
518                         String message = Messages.getMessage( e );
519                         message = Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" );
520 
521                         if ( resource.isOptional() )
522                         {
523                             this.getProject().log( message, e, Project.MSG_WARN );
524                         }
525                         else
526                         {
527                             throw new BuildException( message, e, this.getLocation() );
528                         }
529                     }
530                     catch ( final IOException e )
531                     {
532                         String message = Messages.getMessage( e );
533                         message = Messages.getMessage( "resourceFailure", message != null ? " " + message : "" );
534 
535                         if ( resource.isOptional() )
536                         {
537                             this.getProject().log( message, e, Project.MSG_WARN );
538                         }
539                         else
540                         {
541                             throw new BuildException( message, e, this.getLocation() );
542                         }
543                     }
544                     finally
545                     {
546                         try
547                         {
548                             if ( in != null )
549                             {
550                                 in.close();
551                             }
552                         }
553                         catch ( final IOException e )
554                         {
555                             if ( suppressExceptionOnClose )
556                             {
557                                 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
558                             }
559                             else
560                             {
561                                 throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
562                             }
563                         }
564                     }
565                 }
566 
567                 suppressExceptionOnClose = true;
568             }
569 
570             for ( final String defaultExclude : classLoader.getModletExcludes() )
571             {
572                 final Modlet m = modlets.getModlet( defaultExclude );
573                 if ( m != null )
574                 {
575                     modlets.getModlet().remove( m );
576                 }
577             }
578 
579             modlets.getModlet().addAll( classLoader.getExcludedModlets().getModlet() );
580 
581             for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
582             {
583                 final Modlet modlet = it.next();
584 
585                 if ( !this.isModletIncluded( modlet ) || this.isModletExcluded( modlet ) )
586                 {
587                     it.remove();
588                     this.log( Messages.getMessage( "excludingModlet", modlet.getName() ) );
589                 }
590                 else
591                 {
592                     this.log( Messages.getMessage( "includingModlet", modlet.getName() ) );
593                 }
594             }
595 
596             final ModelValidationReport validationReport =
597                 context.validateModel( ModletObject.MODEL_PUBLIC_ID,
598                                        new JAXBSource( marshaller, new ObjectFactory().createModlets( modlets ) ) );
599 
600             this.logValidationReport( context, validationReport );
601 
602             if ( !validationReport.isModelValid() )
603             {
604                 throw new ModelException( Messages.getMessage( "invalidModel", ModletObject.MODEL_PUBLIC_ID ) );
605             }
606 
607             Modlet mergedModlet = modlets.getMergedModlet( this.getModletName(), this.getModel() );
608             mergedModlet.setVendor( this.getModletVendor() );
609             mergedModlet.setVersion( this.getModletVersion() );
610 
611             for ( int i = 0, s0 = this.getModletObjectStylesheetResources().size(); i < s0; i++ )
612             {
613                 final Transformer transformer =
614                     this.getTransformer( this.getModletObjectStylesheetResources().get( i ) );
615 
616                 if ( transformer != null )
617                 {
618                     final JAXBSource source =
619                         new JAXBSource( marshaller, new ObjectFactory().createModlet( mergedModlet ) );
620 
621                     final JAXBResult result = new JAXBResult( unmarshaller );
622                     transformer.transform( source, result );
623 
624                     if ( result.getResult() instanceof JAXBElement<?>
625                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Modlet )
626                     {
627                         mergedModlet = (Modlet) ( (JAXBElement<?>) result.getResult() ).getValue();
628                     }
629                     else
630                     {
631                         throw new BuildException( Messages.getMessage(
632                             "illegalTransformationResult",
633                             this.getModletObjectStylesheetResources().get( i ).getLocation() ), this.getLocation() );
634 
635                     }
636                 }
637             }
638 
639             this.log( Messages.getMessage( "writingEncoded", this.getModletFile().getAbsolutePath(),
640                                            this.getModletEncoding() ) );
641 
642             marshaller.setProperty( Marshaller.JAXB_ENCODING, this.getModletEncoding() );
643             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
644             marshaller.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
645             marshaller.marshal( new ObjectFactory().createModlet( mergedModlet ), this.getModletFile() );
646             suppressExceptionOnClose = false;
647         }
648         catch ( final URISyntaxException e )
649         {
650             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
651         }
652         catch ( final JAXBException e )
653         {
654             String message = Messages.getMessage( e );
655             if ( message == null )
656             {
657                 message = Messages.getMessage( e.getLinkedException() );
658             }
659 
660             throw new BuildException( message, e, this.getLocation() );
661         }
662         catch ( final TransformerConfigurationException e )
663         {
664             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
665         }
666         catch ( final TransformerException e )
667         {
668             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
669         }
670         catch ( final ModelException e )
671         {
672             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
673         }
674         finally
675         {
676             try
677             {
678                 if ( classLoader != null )
679                 {
680                     classLoader.close();
681                 }
682             }
683             catch ( final IOException e )
684             {
685                 if ( suppressExceptionOnClose )
686                 {
687                     this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
688                 }
689                 else
690                 {
691                     throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
692                 }
693             }
694         }
695     }
696 
697     /**
698      * Tests inclusion of a given modlet based on property {@code modletIncludes}.
699      *
700      * @param modlet The modlet to test.
701      *
702      * @return {@code true}, if {@code modlet} is included based on property {@code modletIncludes}.
703      *
704      * @throws NullPointerException if {@code modlet} is {@code null}.
705      *
706      * @see #getModletIncludes()
707      */
708     public boolean isModletIncluded( final Modlet modlet )
709     {
710         if ( modlet == null )
711         {
712             throw new NullPointerException( "modlet" );
713         }
714 
715         for ( final NameType include : this.getModletIncludes() )
716         {
717             if ( include.getName().equals( modlet.getName() ) )
718             {
719                 return true;
720             }
721         }
722 
723         return this.getModletIncludes().isEmpty() ? true : false;
724     }
725 
726     /**
727      * Tests exclusion of a given modlet based on property {@code modletExcludes}.
728      *
729      * @param modlet The modlet to test.
730      *
731      * @return {@code true}, if {@code modlet} is excluded based on property {@code modletExcludes}.
732      *
733      * @throws NullPointerException if {@code modlet} is {@code null}.
734      *
735      * @see #getModletExcludes()
736      */
737     public boolean isModletExcluded( final Modlet modlet )
738     {
739         if ( modlet == null )
740         {
741             throw new NullPointerException( "modlet" );
742         }
743 
744         for ( final NameType exclude : this.getModletExcludes() )
745         {
746             if ( exclude.getName().equals( modlet.getName() ) )
747             {
748                 return true;
749             }
750         }
751 
752         return false;
753     }
754 
755     /**
756      * {@inheritDoc}
757      */
758     @Override
759     public MergeModletsTask clone()
760     {
761         final MergeModletsTask clone = (MergeModletsTask) super.clone();
762         clone.modletFile = this.modletFile != null ? new File( this.modletFile.getAbsolutePath() ) : null;
763 
764         if ( this.modletResources != null )
765         {
766             clone.modletResources = new HashSet<ModletResourceType>( this.modletResources.size() );
767             for ( final ModletResourceType e : this.modletResources )
768             {
769                 clone.modletResources.add( e.clone() );
770             }
771         }
772 
773         if ( this.modletExcludes != null )
774         {
775             clone.modletExcludes = new HashSet<NameType>( this.modletExcludes.size() );
776             for ( final NameType e : this.modletExcludes )
777             {
778                 clone.modletExcludes.add( e.clone() );
779             }
780         }
781 
782         if ( this.modletIncludes != null )
783         {
784             clone.modletIncludes = new HashSet<NameType>( this.modletIncludes.size() );
785             for ( final NameType e : this.modletIncludes )
786             {
787                 clone.modletIncludes.add( e.clone() );
788             }
789         }
790 
791         if ( this.modletObjectStylesheetResources != null )
792         {
793             clone.modletObjectStylesheetResources =
794                 new ArrayList<TransformerResourceType>( this.modletObjectStylesheetResources.size() );
795 
796             for ( final TransformerResourceType e : this.modletObjectStylesheetResources )
797             {
798                 clone.modletObjectStylesheetResources.add( e.clone() );
799             }
800         }
801 
802         return clone;
803     }
804 
805 }