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