View Javadoc

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