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: MergeModletsTask.java 4200 2012-01-25 09:46:13Z schulte2005 $
029     *
030     */
031    package org.jomc.ant;
032    
033    import java.io.ByteArrayOutputStream;
034    import java.io.File;
035    import java.io.IOException;
036    import java.io.InputStream;
037    import java.io.OutputStreamWriter;
038    import java.net.SocketTimeoutException;
039    import java.net.URISyntaxException;
040    import java.net.URL;
041    import java.net.URLConnection;
042    import java.util.ArrayList;
043    import java.util.HashSet;
044    import java.util.Iterator;
045    import java.util.LinkedList;
046    import java.util.List;
047    import java.util.Set;
048    import java.util.logging.Level;
049    import javax.xml.bind.JAXBElement;
050    import javax.xml.bind.JAXBException;
051    import javax.xml.bind.Marshaller;
052    import javax.xml.bind.Unmarshaller;
053    import javax.xml.bind.util.JAXBResult;
054    import javax.xml.bind.util.JAXBSource;
055    import javax.xml.transform.Source;
056    import javax.xml.transform.Transformer;
057    import javax.xml.transform.TransformerConfigurationException;
058    import javax.xml.transform.TransformerException;
059    import javax.xml.transform.stream.StreamSource;
060    import org.apache.tools.ant.BuildException;
061    import org.apache.tools.ant.Project;
062    import org.jomc.ant.types.ModletResourceType;
063    import org.jomc.ant.types.NameType;
064    import org.jomc.ant.types.ResourceType;
065    import org.jomc.ant.types.TransformerResourceType;
066    import org.jomc.modlet.DefaultModletProvider;
067    import org.jomc.modlet.ModelContext;
068    import org.jomc.modlet.ModelException;
069    import org.jomc.modlet.ModelValidationReport;
070    import org.jomc.modlet.Modlet;
071    import org.jomc.modlet.ModletObject;
072    import org.jomc.modlet.Modlets;
073    import org.jomc.modlet.ObjectFactory;
074    
075    /**
076     * Task for merging modlet resources.
077     *
078     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
079     * @version $JOMC: MergeModletsTask.java 4200 2012-01-25 09:46:13Z schulte2005 $
080     */
081    public final class MergeModletsTask extends JomcTask
082    {
083    
084        /** The encoding of the modlet resource. */
085        private String modletEncoding;
086    
087        /** File to write the merged modlet to. */
088        private File modletFile;
089    
090        /** The name of the merged modlet. */
091        private String modletName;
092    
093        /** The version of the merged modlet. */
094        private String modletVersion;
095    
096        /** The vendor of the merged modlet. */
097        private String modletVendor;
098    
099        /** 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    }