Changeset 1649 for trunk/docx4j/src


Ignore:
Timestamp:
09/02/11 07:16:44 (9 months ago)
Author:
jharrop
Message:

Improved control over what addTargetPart does in the event of a part name collision.

Location:
trunk/docx4j/src
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/docx4j/src/main/java/org/docx4j/openpackaging/Base.java

    r1648 r1649  
    2727import org.docx4j.openpackaging.contenttype.ContentType; 
    2828import org.docx4j.openpackaging.contenttype.ObjectFactory; 
     29import org.docx4j.openpackaging.exceptions.Docx4JException; 
    2930import org.docx4j.openpackaging.exceptions.InvalidFormatException; 
    3031import org.docx4j.openpackaging.packages.OpcPackage; 
     
    3233import org.docx4j.openpackaging.parts.PartName; 
    3334import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; 
     35import org.docx4j.openpackaging.parts.relationships.RelationshipsPart.AddPartBehaviour; 
    3436import org.docx4j.relationships.Relationship; 
    3537 
     
    171173//      } 
    172174 
     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        } 
    173192         
    174193        /** 
     
    187206         */ 
    188207        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 
    192211        /** 
    193212         * Convenience method to add a part to this Part's 
     
    207226                        ) throws InvalidFormatException { 
    208227                 
     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                 
    209254                if ( this.getPackage()==null ) {                                                 
    210255                        throw new InvalidFormatException("Package not set; if you are adding part2 to part1, make sure part1 is added first."); 
     
    215260                 
    216261                // Now add the targetpart to the relationships 
    217                 Relationship rel = this.getRelationshipsPart().addPart(targetpart, true,  
     262                Relationship rel = this.getRelationshipsPart().addPart(targetpart, mode,  
    218263                                getPackage().getContentTypeManager(), proposedRelId); 
    219264                 
     
    227272                 
    228273        } 
    229  
    230          
    231274         
    232275} 
  • trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/Part.java

    r1399 r1649  
    6767        } 
    6868        /** 
     69         * NB a media part could be referenced from multiple 
     70         * source parts, but this method can only record one! 
     71         *  
    6972         * @param sourceRelationship the sourceRelationship to set 
    7073         */ 
  • trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/relationships/RelationshipsPart.java

    r1648 r1649  
    5454import java.net.URISyntaxException; 
    5555import java.util.ArrayList; 
     56import java.util.HashMap; 
    5657import java.util.List; 
    5758 
     
    7879 
    7980 
    80  
    81  
    82  
    8381/** 
    8482 * Represents a Relationship Part, which contains the relationships for a  
    8583 * given PackagePart or the Package. 
    86  *  
    87  * @author Julien Chable, CDubettier 
    88  * @version 0.1 
    8984 */ 
    9085public final class RelationshipsPart extends JaxbXmlPart<Relationships> {  
    91         // implements Iterable<Relationship> { 
    9286 
    9387        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          */ 
    14288         
    14389 
     
    431377                 
    432378                part.setOwningRelationshipPart(this); 
    433                 part.setSourceRelationship(sourceRelationship); 
     379                part.setSourceRelationship(sourceRelationship);   
    434380 
    435381                // All (non-relationship) parts are stored in a collection 
     
    458404         * @return The Relationship 
    459405         */ 
    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); 
    463409        } 
    464410         
     
    477423         * @return The Relationship 
    478424         */ 
    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 
    485467 
    486468                URI tobeRelativized = part.getPartName().getURI(); 
     
    490472                                + " against source " + relativizeAgainst); 
    491473                 
    492                 String result = URIHelper.relativizeURI(relativizeAgainst,  
     474                String target = URIHelper.relativizeURI(relativizeAgainst,  
    493475                                tobeRelativized).toString();  
    494476                 
    495477                if (relativizeAgainst.getPath().equals("/") 
    496                                 && result.startsWith("/")) { 
     478                                && target.startsWith("/")) { 
    497479                         
    498480                        /* 
     
    503485                         */              
    504486                         
    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 
    509536                 
    510537                org.docx4j.relationships.ObjectFactory factory = 
     
    513540                Relationship rel = factory.createRelationship(); 
    514541                 
    515                 rel.setTarget(result.toString() ); 
     542                rel.setTarget(target ); 
    516543                //rel.setTargetMode( TargetMode.INTERNAL ); 
    517544                rel.setType( part.getRelationshipType() ); 
     
    520547                        rel.setId( relId );                      
    521548                } 
    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                 
    566550                addRelationship(rel ); 
    567551                 
     
    580564                } 
    581565                 
    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                 
    584595        } 
    585596         
     
    594605                throws InvalidOperationException { 
    595606 
     607                // ECMA-376 Part 2 8.3 says that Id must be unique. 
     608                 
    596609                if ( rel.getId()==null) { 
    597610                        String id = getNextId(); 
     
    710723         */ 
    711724        public Relationship getRel(PartName partName) { // introduced after 2.6.0 
     725 
    712726                 
    713727                for (Relationship rel : jaxbElement.getRelationship() ) { 
     
    719733//                              continue; 
    720734//                      } 
    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                         
    722740                        if (isTarget(partName, rel) ) { 
    723741                                return rel; 
     
    729747        /** 
    730748         * Is partName the target of the specified rel? 
     749         *  
     750         * @since 2.6.0 
     751         *  
    731752         * @param partName 
    732753         * @param rel 
     
    9961017        } 
    9971018         
     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         
    9981058//      public static void main(String[] args) throws Exception { 
    9991059//               
Note: See TracChangeset for help on using the changeset viewer.