Changeset 1649 for trunk/docx4j/src
- Timestamp:
- 09/02/11 07:16:44 (9 months ago)
- Location:
- trunk/docx4j/src
- Files:
-
- 1 added
- 3 edited
-
main/java/org/docx4j/openpackaging/Base.java (modified) (7 diffs)
-
main/java/org/docx4j/openpackaging/parts/Part.java (modified) (1 diff)
-
main/java/org/docx4j/openpackaging/parts/relationships/RelationshipsPart.java (modified) (15 diffs)
-
test/java/org/docx4j/openpackaging/parts/relationships/AddPartTests.java (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/docx4j/src/main/java/org/docx4j/openpackaging/Base.java
r1648 r1649 27 27 import org.docx4j.openpackaging.contenttype.ContentType; 28 28 import org.docx4j.openpackaging.contenttype.ObjectFactory; 29 import org.docx4j.openpackaging.exceptions.Docx4JException; 29 30 import org.docx4j.openpackaging.exceptions.InvalidFormatException; 30 31 import org.docx4j.openpackaging.packages.OpcPackage; … … 32 33 import org.docx4j.openpackaging.parts.PartName; 33 34 import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; 35 import org.docx4j.openpackaging.parts.relationships.RelationshipsPart.AddPartBehaviour; 34 36 import org.docx4j.relationships.Relationship; 35 37 … … 171 173 // } 172 174 175 /** 176 * Convenience method to add a part to this Part's 177 * relationships. The package must be set on this 178 * part in order for this to work. 179 * 180 * @since 2.7.1 181 * 182 * @param targetpart The part to add to this part's relationships 183 * @param mode whether to overwrite, rename or abort if the part name already exists 184 * @param proposedRelId 185 * @return 186 * @throws InvalidFormatException 187 */ 188 public Relationship addTargetPart(Part targetpart, AddPartBehaviour mode 189 ) throws InvalidFormatException { 190 return addTargetPart(targetpart, mode, null); 191 } 173 192 174 193 /** … … 187 206 */ 188 207 public Relationship addTargetPart(Part targetpart) throws InvalidFormatException { 189 return this.addTargetPart(targetpart, null);190 } 191 208 return this.addTargetPart(targetpart, AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS, null); 209 } 210 192 211 /** 193 212 * Convenience method to add a part to this Part's … … 207 226 ) throws InvalidFormatException { 208 227 228 return addTargetPart( targetpart, AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS, proposedRelId); 229 } 230 231 232 /** 233 * Convenience method to add a part to this Part's 234 * relationships. The package must be set on this 235 * part in order for this to work. 236 * 237 * The added part will replace any existing part 238 * with the same name (ie same target in the rels 239 * part). In other words, if you want to use the 240 * one image as the target of 2 rels, don't use 241 * this method. 242 * 243 * @since 2.7.1 244 * 245 * @param targetpart The part to add to this part's relationships 246 * @param mode whether to overwrite, rename or abort if the part name already exists 247 * @param proposedRelId 248 * @return 249 * @throws InvalidFormatException 250 */ 251 public Relationship addTargetPart(Part targetpart, AddPartBehaviour mode, String proposedRelId 252 ) throws InvalidFormatException { 253 209 254 if ( this.getPackage()==null ) { 210 255 throw new InvalidFormatException("Package not set; if you are adding part2 to part1, make sure part1 is added first."); … … 215 260 216 261 // Now add the targetpart to the relationships 217 Relationship rel = this.getRelationshipsPart().addPart(targetpart, true,262 Relationship rel = this.getRelationshipsPart().addPart(targetpart, mode, 218 263 getPackage().getContentTypeManager(), proposedRelId); 219 264 … … 227 272 228 273 } 229 230 231 274 232 275 } -
trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/Part.java
r1399 r1649 67 67 } 68 68 /** 69 * NB a media part could be referenced from multiple 70 * source parts, but this method can only record one! 71 * 69 72 * @param sourceRelationship the sourceRelationship to set 70 73 */ -
trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/relationships/RelationshipsPart.java
r1648 r1649 54 54 import java.net.URISyntaxException; 55 55 import java.util.ArrayList; 56 import java.util.HashMap; 56 57 import java.util.List; 57 58 … … 78 79 79 80 80 81 82 83 81 /** 84 82 * Represents a Relationship Part, which contains the relationships for a 85 83 * given PackagePart or the Package. 86 *87 * @author Julien Chable, CDubettier88 * @version 0.189 84 */ 90 85 public final class RelationshipsPart extends JaxbXmlPart<Relationships> { 91 // implements Iterable<Relationship> {92 86 93 87 private static Logger log = Logger.getLogger(RelationshipsPart.class); 94 95 /* Example:96 *97 * Package relationships:98 *99 * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>100 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">101 <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>102 <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>103 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>104 </Relationships>105 106 107 word/_rels/document.xml.rels:108 109 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>110 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">111 <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/>112 <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>113 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>114 <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>115 <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/>116 </Relationships>117 118 More complex version:119 120 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">121 <Relationship Id="rId1" Target="customXml/item1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"/>122 <Relationship Id="rId10" Target="header2.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"/>123 <Relationship Id="rId11" Target="footer1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"/>124 <Relationship Id="rId12" Target="footer2.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"/>125 <Relationship Id="rId13" Target="header3.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"/>126 <Relationship Id="rId14" Target="footer3.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"/>127 <Relationship Id="rId15" Target="fontTable.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable"/>128 <Relationship Id="rId16" Target="glossary/document.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/glossaryDocument"/>129 <Relationship Id="rId17" Target="theme/theme1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"/>130 <Relationship Id="rId2" Target="numbering.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"/>131 <Relationship Id="rId3" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>132 <Relationship Id="rId4" Target="settings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"/>133 <Relationship Id="rId5" Target="webSettings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings"/>134 <Relationship Id="rId6" Target="footnotes.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"/>135 <Relationship Id="rId7" Target="endnotes.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"/>136 <Relationship Id="rId8" Target="comments.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"/>137 <Relationship Id="rId9" Target="header1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"/>138 </Relationships>139 140 141 */142 88 143 89 … … 431 377 432 378 part.setOwningRelationshipPart(this); 433 part.setSourceRelationship(sourceRelationship); 379 part.setSourceRelationship(sourceRelationship); 434 380 435 381 // All (non-relationship) parts are stored in a collection … … 458 404 * @return The Relationship 459 405 */ 460 public Relationship addPart(Part part, boolean overwriteExistingTarget,461 ContentTypeManager ctm) {462 return this.addPart(part, overwriteExistingTarget, ctm, null);406 public Relationship addPart(Part part, AddPartBehaviour mode, 407 ContentTypeManager ctm) throws InvalidFormatException { 408 return this.addPart(part, mode, ctm, null); 463 409 } 464 410 … … 477 423 * @return The Relationship 478 424 */ 479 public Relationship addPart(Part part, boolean overwriteExistingTarget, 480 ContentTypeManager ctm, String relId) { 481 482 log.info("adding part: " + part.getPartName().getName() ); 483 484 // Now add a new relationship 425 public Relationship addPart(Part part, AddPartBehaviour mode, 426 ContentTypeManager ctm, String relId) throws InvalidFormatException { 427 428 PartName newPartName = part.getPartName(); 429 log.info("adding part with proposed name: " + newPartName.getName()); 430 431 if (this.getPackage().getParts().get( newPartName )!=null) { 432 433 if (mode.equals(AddPartBehaviour.REUSE_EXISTING)) { 434 435 part = this.getPackage().getParts().get( newPartName ); 436 437 // if rel already exists, return that 438 // if rel doesn't exist (the part does), create a rel to it 439 } 440 441 if (mode.equals(AddPartBehaviour.RENAME_IF_NAME_EXISTS)) { 442 443 // it is not enough to be unique in just the rp (eg media parts) 444 445 String proposedName = part.getPartName().getName(); 446 log.debug("Detected duplicate partname: " + proposedName ); 447 if (proposedName.indexOf(".")>0) { 448 // TODO: strip trailing numerals off prefix 449 // eg footer1 should become footer 450 newPartName = getNewPartName( proposedName.substring(0, proposedName.indexOf(".")-1), 451 "." + part.getPartName().getExtension(), 452 this.getPackage().getParts().getParts() ); 453 } else { 454 newPartName = getNewPartName( proposedName, 455 "." , 456 this.getPackage().getParts().getParts() ); 457 } 458 part.partName = newPartName; // access directly 459 460 // this partname is globally unique in the docx 461 // so rel won't exist 462 463 } 464 } 465 466 // First work out what the target would be 485 467 486 468 URI tobeRelativized = part.getPartName().getURI(); … … 490 472 + " against source " + relativizeAgainst); 491 473 492 String result = URIHelper.relativizeURI(relativizeAgainst,474 String target = URIHelper.relativizeURI(relativizeAgainst, 493 475 tobeRelativized).toString(); 494 476 495 477 if (relativizeAgainst.getPath().equals("/") 496 && result.startsWith("/")) {478 && target.startsWith("/")) { 497 479 498 480 /* … … 503 485 */ 504 486 505 result = result.substring(1); 506 } 507 508 log.debug("Result " + result); 487 target = target.substring(1); 488 } 489 490 log.debug("Result " + target); 491 492 // Check whether we already have a rel with this target 493 // This code is a bit more efficient than getRel, since it 494 // doesn't unrelativise each existing rel! 495 Relationship existsAlready = null; 496 for (Relationship rel : jaxbElement.getRelationship() ) { 497 if (rel.getTarget().equals(target)) { 498 existsAlready = rel; 499 break; // Assumes at most 1 existing rel with this target 500 } 501 } 502 503 if (existsAlready!=null 504 && mode.equals(AddPartBehaviour.REUSE_EXISTING)) { 505 log.debug("Returning preexisting rel"); 506 return existsAlready; 507 } 508 509 // sanity check 510 if (existsAlready!=null && mode.equals(AddPartBehaviour.RENAME_IF_NAME_EXISTS)) { 511 // Shouldn't happen 512 throw new InvalidFormatException("Found existing rel, and yet constructed part name should be globally unique!"); 513 } 514 515 if (this.getPackage().getParts().get( newPartName )!=null 516 && mode.equals(AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS)) { 517 518 // ie we have the same part in the package already, not 519 // necessarily referenced by a rel in this rp. 520 521 // overwrite the part 522 if (existsAlready!=null) { 523 524 // we reuse it. can't assume its the same type, though, so 525 existsAlready.setType( part.getRelationshipType() ); 526 527 loadPart(part, existsAlready); 528 return existsAlready; 529 } 530 531 // case where rel doesn't exist (it might not in this rp), create a rel to it 532 // is handled below 533 } 534 535 // OK, create the new rel 509 536 510 537 org.docx4j.relationships.ObjectFactory factory = … … 513 540 Relationship rel = factory.createRelationship(); 514 541 515 rel.setTarget( result.toString());542 rel.setTarget(target ); 516 543 //rel.setTargetMode( TargetMode.INTERNAL ); 517 544 rel.setType( part.getRelationshipType() ); … … 520 547 rel.setId( relId ); 521 548 } 522 523 loadPart(part, rel); 524 525 if (overwriteExistingTarget) { 526 // Is more than one rel with the same target 527 // ever permitted. For example, an image? 528 // ECMA-376 Part 2 8.3 only says that 529 // Id must be unique. 530 // (But we don't test for that here; the id 531 // is only assigned in addRelationship further 532 // below) 533 534 // Word fails to load a document if it has 2 copies of the styles part 535 // (each with a separate rels entry). 536 537 // We ensure there is just a single entry 538 // iff overwriteExistingTarget is set 539 // NB, this does not recursively remove parts 540 // (compare removePart method below) 541 // In fact, it only removes the rel, it leaves the 542 // part in the Parts hashmap, but that's ok 543 // since the docx is constructed by walking the 544 // rels tree. And loadPart above will overwrite 545 // any existing part which has the same name. 546 547 548 Relationship relToBeRemoved = null; 549 for (Relationship relic : jaxbElement.getRelationship() ) { 550 551 if (relic.getTarget().equals( rel.getTarget() )) { 552 553 log.info("True - will delete relationship with target " + rel.getTarget()); 554 relToBeRemoved = relic; // Avoid java.util.ConcurrentModificationException 555 break; 556 } 557 558 } 559 if (relToBeRemoved!=null) { 560 removeRelationship(relToBeRemoved); 561 } 562 } 563 564 // Relationship rel = new Relationship(sourceP, result, 565 // TargetMode.INTERNAL, part.getRelationshipType(), id); 549 566 550 addRelationship(rel ); 567 551 … … 580 564 } 581 565 582 return rel; 583 566 if (this.getPackage().getParts().get( newPartName )!=null) { 567 568 if (mode.equals(AddPartBehaviour.REUSE_EXISTING)) { 569 // just return rel; 570 } 571 572 if (mode.equals(AddPartBehaviour.RENAME_IF_NAME_EXISTS)) { 573 loadPart(part, rel); 574 } 575 576 if (mode.equals(AddPartBehaviour.OVERWRITE_IF_NAME_EXISTS)) { 577 loadPart(part, rel); 578 } 579 return rel; 580 581 } else { 582 // Usual case 583 584 loadPart(part, rel); 585 return rel; 586 587 } 588 589 // Is more than one rel with the same target 590 // ever permitted. For example, an image? YES 591 592 // Word fails to load a document if it has 2 copies of the styles part 593 // (each with a separate rels entry). 594 584 595 } 585 596 … … 594 605 throws InvalidOperationException { 595 606 607 // ECMA-376 Part 2 8.3 says that Id must be unique. 608 596 609 if ( rel.getId()==null) { 597 610 String id = getNextId(); … … 710 723 */ 711 724 public Relationship getRel(PartName partName) { // introduced after 2.6.0 725 712 726 713 727 for (Relationship rel : jaxbElement.getRelationship() ) { … … 719 733 // continue; 720 734 // } 721 735 736 // TODO 20110902: it would be more efficient to relativise the partName 737 // just once, and compare using that (see addPart, which 738 // works this way). 739 722 740 if (isTarget(partName, rel) ) { 723 741 return rel; … … 729 747 /** 730 748 * Is partName the target of the specified rel? 749 * 750 * @since 2.6.0 751 * 731 752 * @param partName 732 753 * @param rel … … 996 1017 } 997 1018 1019 1020 private PartName getNewPartName(String prefix, String suffix, 1021 HashMap<PartName, Part> parts) throws InvalidFormatException { 1022 1023 PartName proposed = null; 1024 int i=1; 1025 do { 1026 1027 if (i>1) { 1028 proposed = new PartName( prefix + i + suffix); 1029 } else { 1030 proposed = new PartName( prefix + suffix); 1031 } 1032 i++; 1033 1034 } while (parts.get(proposed)!=null); 1035 1036 return proposed; 1037 1038 } 1039 1040 1041 public enum AddPartBehaviour { 1042 1043 OVERWRITE_IF_NAME_EXISTS("overwrite"), 1044 REUSE_EXISTING("reuse"), 1045 RENAME_IF_NAME_EXISTS("rename"); 1046 1047 private final String value; 1048 1049 AddPartBehaviour(String v) { 1050 value = v; 1051 } 1052 1053 public String value() { 1054 return value; 1055 } 1056 } 1057 998 1058 // public static void main(String[] args) throws Exception { 999 1059 //
Note: See TracChangeset
for help on using the changeset viewer.
